home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 1 / PC Actual CD 01.iso / f1 / tutorc.arj / EJEMC.DAT < prev    next >
Encoding:
Text File  |  1993-06-04  |  392.4 KB  |  12,683 lines

  1. ;
  2. ; EJEMPLOS DEL LENGUAJE C
  3. ;
  4.  
  5. ; LECCION 1
  6. begin
  7. begine " OPERACIONES ARITMETICAS "
  8. /*
  9.   El siguiente programa calcula la suma, diferencia, producto, cociente y
  10.   resto de dos números enteros.
  11.   Una vez calculado estos valores los imprime y espera a que el usuario
  12.   pulsa la tecla RETURN para finalizar.
  13. */
  14.  
  15. #include <stdio.h> /* para utilizar: printf (), getchar () */
  16.  
  17. void main (void)
  18. {
  19.   int numero1, numero2;
  20.   int suma, diferencia, producto, cociente, resto;
  21.  
  22.   numero1 = 10;
  23.   numero2 = 3;
  24.   suma = numero1 + numero2;
  25.   diferencia = numero1 - numero2;
  26.   producto = numero1 * numero2;
  27.   cociente = numero1 / numero2;
  28.   resto = numero1 % numero2;
  29.  
  30.   printf ("OPERACIONES ARITMETICAS SOBRE DOS NUMEROS ENTEROS:");
  31.   printf ("\n\nNúmero 1: %d", numero1);
  32.   printf ("\nNúmero 2: %d", numero2);
  33.   printf ("\n\nSuma: %d", suma);
  34.   printf ("\nDiferencia: %d", diferencia);
  35.   printf ("\nProducto: %d", producto);
  36.   printf ("\nCociente: %d", cociente);
  37.   printf ("\nResto: %d", resto);
  38.  
  39.   printf ("\n\nPulsa la tecla RETURN para terminar programa.");
  40.   getchar ();
  41. }
  42. ende
  43. begine " PASOS EN EL DESARROLLO DE UN PROGRAMA "
  44. /*
  45.   Este programa escribe un esquema en pantalla.
  46. */
  47.  
  48. #include <stdio.h> /* para poder utilizar las funciones printf() y getchar() */
  49.  
  50. void main (void)
  51. {
  52.   printf ("                    FASES EN EL DESARROLLO DE UN PROGRAMA\n");
  53.   printf ("                                                         \n");
  54.   printf ("                               ┌───────────┐             \n");
  55.   printf ("                               │  EDICION  │             \n");
  56.   printf ("                               └───────────┘             \n");
  57.   printf ("                                     │                   \n");
  58.   printf ("                              programa fuente            \n");
  59.   printf ("                                     │                   \n");
  60.   printf ("                               ┌───────────┐             \n");
  61.   printf ("                               │COMPILACION│             \n");
  62.   printf ("                               └───────────┘             \n");
  63.   printf ("                                     │                   \n");
  64.   printf ("                              programa objeto            \n");
  65.   printf ("                                     │                   \n");
  66.   printf ("                               ┌───────────┐             \n");
  67.   printf ("                               │ ENLAZADO  │             \n");
  68.   printf ("                               └───────────┘             \n");
  69.   printf ("                                     │                   \n");
  70.   printf ("                             programa ejecutable         \n");
  71.   printf ("                                     │                   \n");
  72.   printf ("                               ┌───────────┐             \n");
  73.   printf ("                               │ EJECUCION │             \n");
  74.   printf ("                               └───────────┘             \n");
  75.   getchar ();
  76. }
  77. ende
  78. end
  79.  
  80. ; LECCION 2
  81. begin
  82. begine " CALCULO DE LA LONGITUD Y EL AREA DE UN CIRCULO "
  83. /*
  84.   El siguiente programa calcula la longitud y el área de un círculo de radio 2.5
  85. */
  86.  
  87. #include <stdio.h> // printf (), getchar ()
  88.  
  89. void main (void)
  90. {
  91.   const float pi = 3.141592;
  92.   float radio, longitud, area;
  93.  
  94.   radio = 2.5;
  95.   longitud = 2 * pi * radio;
  96.   area = pi * radio * radio;
  97.  
  98.   printf ("Cálculo de la longitud y área de un círculo de radio %f:\n\n", radio);
  99.   printf ("\tLONGITUD: %f\n", longitud);
  100.   printf ("\tAREA    : %f\n", area);
  101.  
  102.   printf ("\nPulsa la tecla RETURN para salir.\n");
  103.   getchar ();
  104. }
  105. ende
  106. begine " TAMAÑO DE LOS TIPOS MAS USADOS EN C "
  107. /*
  108.   Este programa muestra el tamaño de los tipos más usados en C en el
  109.   sistema en el cual se ejecuta el programa.
  110. */
  111.  
  112. #include <stdio.h> /* printf(), getchar() */
  113.  
  114. void main (void)
  115. {
  116.   printf ("TAMAÑO DE ALGUNOS TIPOS EN EL SISTEMA EN QUE SE EJECUTA EL PROGRAMA:\n\n");
  117.   printf ("  Tipo          Tamaño (en bytes)\n");
  118.   printf ("  ------------  -----------------\n");
  119.   printf ("  char                 %d        \n", sizeof (char));
  120.   printf ("  int                  %d        \n", sizeof (int));
  121.   printf ("  short int            %d        \n", sizeof (short int));
  122.   printf ("  long int             %d        \n", sizeof (long int));
  123.   printf ("  float                %d        \n", sizeof (float));
  124.   printf ("  double               %d        \n", sizeof (double));
  125.   printf ("  long double          %d        \n", sizeof (long double));
  126.   printf ("\nPulsa la tecla RETURN para salir.\n");
  127.   getchar ();
  128. }
  129. ende
  130. end
  131.  
  132. ; LECCION 3
  133. begin
  134. begine " ORDENACION POR EL METODO DE INSERCION DIRECTA "
  135. /*
  136.   En este programa se realiza la ordenación creciente de un vector de números
  137.   enteros.
  138.  
  139.   El método de ordenación seguido ha sido el de inserción directa (denominado
  140.   también método de la baraja).
  141.  
  142.   Supongamos un vector v de n componentes. El método consiste en repetir el
  143.   siguiente proceso desde la segunda componente hasta la última: Se toma
  144.   dicha componente y se inserta en el lugar que le corresponda entre las
  145.   componentes situadas a su izquierda (éstas ya estarán ordenadas). Las
  146.   componentes superiores a la tratada se desplazarán un lugar a la derecha.
  147.  
  148.   Ejemplo:
  149.  
  150.     Secuencia inicial   7   5   1   2   4
  151.     Primer paso         5   7   1   2   4
  152.     Segundo paso        1   5   7   2   4
  153.     Tercer paso         1   2   5   7   4
  154.     Cuarto paso         1   2   4   5   7
  155.  
  156.   En este programa aparece una función que no se ha visto en teoría: rand ().
  157.   Esta función devuelve un valor aleatorio entre 0 y RAND_MAX. Tanto la función
  158.   rand () como la constante RAND_MAX están declaradas en la librería stdlib.h.
  159.   El valor de RAND_MAX suele ser el valor del mayor número int con signo.
  160. */
  161.  
  162. #include <stdio.h>  /* printf (), getchar () */
  163. #include <stdlib.h> /* rand ()               */
  164.  
  165. void main (void)
  166. {
  167.   int vector [100]; /* declaración de un vector de 100 elementos int */
  168.   int i, j, aux; /* declaración de tres variables int: dos de índice y una auxiliar */
  169.  
  170.   /* Escritura de la cabecera. */
  171.   printf ("ORDENACION DE UN VECTOR DESORDENADO POR EL METODO DE INSERCION DIRECTA:");
  172.  
  173.   /* Relleno aleatorio de los 100 componentes del vector. A cada componente se
  174.      le asigna un valor aleatorio entre 0 y 999. Las componentes del vector van
  175.      desde la 0 hasta la 99 inclusive. */
  176.   for (i = 0; i <= 99; i++)
  177.     vector[i] = rand () % 1000;
  178.  
  179.   /* Escritura en pantalla del vector desordenado. */
  180.   printf ("\n   Vector desordenado:\n");
  181.   for (i = 0; i < 100; i++)
  182.     printf ("%d\t", vector[i]);
  183.  
  184.   /* Ordenación del vector por el método de inserción directa. */
  185.   for (i = 1; i <= 99; i++)
  186.     {
  187.       aux = vector[i];
  188.       for (j = i - 1; vector[j] > aux && j > 0; j--)
  189.         vector[j+1] = vector[j];
  190.       if (vector[j] > aux)
  191.         {
  192.           vector[j+1] = vector[j];
  193.           vector[j] = aux;
  194.         }
  195.       else
  196.         vector[j+1] = aux;
  197.     }
  198.  
  199.   /* Escritura en pantalla del vector ordenado. */
  200.   printf ("\n   Vector ordenado:\n");
  201.   for (i = 0; i < 100; i++)
  202.     printf ("%d\t", vector[i]);
  203.  
  204.   /* Espera la pulsación de la tecla RETURN para finalizar el programa. */
  205.   getchar ();
  206. }
  207. ende
  208. begine " ORDENACION POR EL METODO DE SELECCION DIRECTA "
  209. /*
  210.   En este programa se realiza la ordenación creciente de un vector de números
  211.   enteros.
  212.  
  213.   El método de ordenación seguido ha sido el de selección directa.
  214.  
  215.   Supongamos un vector v de n componentes. El método consiste en repetir el
  216.   siguiente proceso desde el primer elemento hasta el penúltimo: Se selecciona
  217.   la componente de menor valor de todas las situadas a la derecha de la tratada
  218.   y se intercambia con ésta.
  219.  
  220.   Ejemplo:
  221.  
  222.     Secuencia inicial   7   5   1   2   4
  223.     Primer paso         1   5   7   2   4
  224.     Segundo paso        1   2   7   5   4
  225.     Tercer paso         1   2   4   5   7
  226.     Cuarto paso         1   2   4   5   7
  227.  
  228.   En este programa aparece una función que no se ha visto en teoría: rand ().
  229.   Esta función devuelve un valor aleatorio entre 0 y RAND_MAX. Tanto la función
  230.   rand () como la constante RAND_MAX están declaradas en la librería stdlib.h.
  231.   El valor de RAND_MAX suele ser el valor del mayor número int con signo.
  232. */
  233.  
  234. #include <stdio.h>  /* printf (), getchar () */
  235. #include <stdlib.h> /* rand ()               */
  236.  
  237. void main (void)
  238. {
  239.   int vector [100]; /* declaración de un vector de 100 elementos int */
  240.   int i, j, k, aux; /* declaración de cuatro variables int: tres de índice y una auxiliar */
  241.  
  242.   /* Escritura de la cabecera. */
  243.   printf ("ORDENACION DE UN VECTOR DESORDENADO POR EL METODO DE SELECCION DIRECTA:");
  244.  
  245.   /* Relleno aleatorio de los 100 componentes del vector. A cada componente se
  246.      le asigna un valor aleatorio entre 0 y 999. Las componentes del vector van
  247.      desde la 0 hasta la 99 inclusive. */
  248.   for (i = 0; i <= 99; i++)
  249.     vector[i] = rand () % 1000;
  250.  
  251.   /* Escritura en pantalla del vector desordenado. */
  252.   printf ("\n   Vector desordenado:\n");
  253.   for (i = 0; i < 100; i++)
  254.     printf ("%d\t", vector[i]);
  255.  
  256.   /* Ordenación del vector por el método de selección directa. */
  257.   for (i = 0; i <= 98; i++)
  258.     {
  259.       k = i;
  260.       aux = vector[i];
  261.       for (j = i + 1; j <= 99; j++)
  262.         if (vector[j] < aux)
  263.           {
  264.             k = j;
  265.             aux = vector[j];
  266.           }
  267.       vector[k] = vector[i];
  268.       vector[i] = aux;
  269.     }
  270.  
  271.   /* Escritura en pantalla del vector ordenado. */
  272.   printf ("\n   Vector ordenado:\n");
  273.   for (i = 0; i < 100; i++)
  274.     printf ("%d\t", vector[i]);
  275.  
  276.   /* Espera la pulsación de la tecla RETURN para finalizar el programa. */
  277.   getchar ();
  278. }
  279. ende
  280. begine " ORDENACION POR EL METODO DE LA BURBUJA "
  281. /*
  282.   En este programa se realiza la ordenación creciente de un vector de números
  283.   enteros.
  284.  
  285.   El método de ordenación seguido ha sido el de la burbuja. Es un método de
  286.   intercambio directo.
  287.  
  288.   Este método tiene dos versiones basadas en la misma idea que consiste en
  289.   recorrer sucesivamente el vector comparando los elementos consecutivos e
  290.   intercambiándolos cuando estén descolocados. El recorrido del vector se
  291.   puede hacer de izquierda a derecha (desplazando los valores mayores hacia
  292.   su derecha) o de derecha a izquierda (desplazando los valores menores
  293.   hacia su izquierda), ambos para la clasificación en orden ascendente.
  294.  
  295.   Recorrido izquierda-derecha: Esta versión del método va colocando en cada
  296.   pasada el mayor elemento de los tratados en la última posición quedando
  297.   colocado y por lo tanto excluido de los elementos a tratar en la siguiente
  298.   pasada.
  299.  
  300.   Ejemplo:
  301.  
  302.     Secuencia inicial   7   5   1   2   4
  303.     Primer paso         5   1   2   4   7
  304.     Segundo paso        1   2   4   5   7
  305.     Tercer paso         1   2   4   5   7
  306.     Cuarto paso         1   2   4   5   7
  307.  
  308.   Recorrido derecha-izquierda: Esta versión es simétrica a la anterior
  309.   desplazando los elementos menores hacia la izquiera.
  310.  
  311.   Ejemplo:
  312.  
  313.     Secuencia inicial   7   5   1   2   4
  314.     Primer paso         1   7   5   2   4
  315.     Segundo paso        1   2   7   5   4
  316.     Tercer paso         1   2   4   7   5
  317.     Cuarto paso         1   2   4   5   7
  318.  
  319.   En este programa aparece una función que no se ha visto en teoría: rand ().
  320.   Esta función devuelve un valor aleatorio entre 0 y RAND_MAX. Tanto la función
  321.   rand () como la constante RAND_MAX están declaradas en la librería stdlib.h.
  322.   El valor de RAND_MAX suele ser el valor del mayor número int con signo.
  323. */
  324.  
  325. #include <stdio.h>  /* printf (), getchar () */
  326. #include <stdlib.h> /* rand ()               */
  327.  
  328. void main (void)
  329. {
  330.   int vector [100]; /* declaración de un vector de 100 elementos int */
  331.   int i, j, aux; /* declaración de tres variables int: dos de índice y una auxiliar */
  332.  
  333.   /* Escritura de la cabecera. */
  334.   printf ("ORDENACION DE UN VECTOR DESORDENADO POR EL METODO DE LA BURBUJA:");
  335.  
  336.   /* Relleno aleatorio de los 100 componentes del vector. A cada componente se
  337.      le asigna un valor aleatorio entre 0 y 999. Las componentes del vector van
  338.      desde la 0 hasta la 99 inclusive. */
  339.   for (i = 0; i <= 99; i++)
  340.     vector[i] = rand () % 1000;
  341.  
  342.   /* Escritura en pantalla del vector desordenado. */
  343.   printf ("\n   Vector desordenado:\n");
  344.   for (i = 0; i < 100; i++)
  345.     printf ("%d\t", vector[i]);
  346.  
  347.   /* Ordenación del vector por el método de intercambio directo (de la burbuja)
  348.      con recorrido de izquierda a derecha. */
  349.   for (i = 98; i >= 0; i--)
  350.     for (j = 0; j <= i; j++)
  351.       if (vector[j] > vector[j+1])
  352.         {
  353.           aux = vector[j];
  354.           vector[j] = vector[j+1];
  355.           vector[j+1] = aux;
  356.         }
  357.  
  358.   /* Ordenación del vector por el método de intercambio directo (de la burbuja)
  359.      con recorrido de derecha a izquierda. */
  360.   for (i = 1; i <= 99; i++)
  361.     for (j = 99; j >= i; j--)
  362.       if (vector[j-1] > vector[j])
  363.         {
  364.           aux = vector[j-1];
  365.           vector[j-1] = vector[j];
  366.           vector[j] = aux;
  367.         }
  368.  
  369.   /* Escritura en pantalla del vector ordenado. */
  370.   printf ("\n   Vector ordenado:\n");
  371.   for (i = 0; i < 100; i++)
  372.     printf ("%d\t", vector[i]);
  373.  
  374.   /* Espera la pulsación de la tecla RETURN para finalizar el programa. */
  375.   getchar ();
  376. }
  377. ende
  378. begine " ORDENACION POR EL METODO DE LA SACUDIDA "
  379. /*
  380.   En este programa se realiza la ordenación creciente de un vector de números
  381.   enteros.
  382.  
  383.   El método de ordenación seguido ha sido el de la sacudida. Es un método de
  384.   intercambio directo.
  385.  
  386.   Consiste en mezclar las dos versiones del método de la burbuja alternativamente,
  387.   de tal forma que se realiza una pasada de derecha a izquierda y a continuación
  388.   otra de izquierda a derecha, recortándose los elementos a tratar por ambos lados
  389.   del vector.
  390.  
  391.   Este método es más eficiente que el de la burbuja en la mayoría de los casos.
  392.  
  393.   Ejemplo:
  394.  
  395.     Secuencia inicial   7   5   1   2   4
  396.     Primer paso         1   7   5   2   4
  397.     Segundo paso        1   5   2   4   7
  398.     Tercer paso         1   2   5   4   7
  399.     Cuarto paso         1   2   4   5   7
  400.  
  401.   En este programa aparece una función que no se ha visto en teoría: rand ().
  402.   Esta función devuelve un valor aleatorio entre 0 y RAND_MAX. Tanto la función
  403.   rand () como la constante RAND_MAX están declaradas en la librería stdlib.h.
  404.   El valor de RAND_MAX suele ser el valor del mayor número int con signo.
  405. */
  406.  
  407. #include <stdio.h>  /* printf (), getchar () */
  408. #include <stdlib.h> /* rand ()               */
  409.  
  410. void main (void)
  411. {
  412.   int vector [100]; /* declaración de un vector de 100 elementos int */
  413.   int i, j, izq, der, aux; /* declaración de cinco variables int */
  414.  
  415.   /* Escritura de la cabecera. */
  416.   printf ("ORDENACION DE UN VECTOR DESORDENADO POR EL METODO DE LA SACUDIDA:");
  417.  
  418.   /* Relleno aleatorio de los 100 componentes del vector. A cada componente se
  419.      le asigna un valor aleatorio entre 0 y 999. Las componentes del vector van
  420.      desde la 0 hasta la 99 inclusive. */
  421.   for (i = 0; i <= 99; i++)
  422.     vector[i] = rand () % 1000;
  423.  
  424.   /* Escritura en pantalla del vector desordenado. */
  425.   printf ("\n   Vector desordenado:\n");
  426.   for (i = 0; i < 100; i++)
  427.     printf ("%d\t", vector[i]);
  428.  
  429.   /* Ordenación del vector por el método de selección directa. */
  430.   izq = 1;
  431.   der = 99;
  432.   do
  433.     {
  434.       for (j = der; j >= izq; j--)
  435.         if (vector[j-1] > vector[j])
  436.           {
  437.             aux = vector[j-1];
  438.             vector[j-1] = vector[j];
  439.             vector[j] = aux;
  440.           }
  441.       izq++;
  442.       for (j = izq; j <= der; j++)
  443.         if (vector[j-1] > vector[j])
  444.           {
  445.             aux = vector[j-1];
  446.             vector[j-1] = vector[j];
  447.             vector[j] = aux;
  448.           }
  449.       der--;
  450.     } while (izq <= der);
  451.  
  452.   /* Escritura en pantalla del vector ordenado. */
  453.   printf ("\n   Vector ordenado:\n");
  454.   for (i = 0; i < 100; i++)
  455.     printf ("%d\t", vector[i]);
  456.  
  457.   /* Espera la pulsación de la tecla RETURN para finalizar el programa. */
  458.   getchar ();
  459. }
  460. ende
  461. begine "ORDENACION POR EL METODO DE INTERCAMBIO DIRECTO CON TEST DE COMPROBACION"
  462. /*
  463.   En este programa se realiza la ordenación creciente de un vector de números
  464.   enteros.
  465.  
  466.   El método de ordenación seguido ha sido el de intercambio directo con test
  467.   de comprobacion.
  468.  
  469.   Es una mejora de los métodos de intercambio directo en la que se comprueba
  470.   mediante un interruptor si el vector está totalmente ordenado después de
  471.   cada pasada, terminando la ejecución en caso afirmativo.
  472.  
  473.   En este programa aparece una función que no se ha visto en teoría: rand ().
  474.   Esta función devuelve un valor aleatorio entre 0 y RAND_MAX. Tanto la función
  475.   rand () como la constante RAND_MAX están declaradas en la librería stdlib.h.
  476.   El valor de RAND_MAX suele ser el valor del mayor número int con signo.
  477. */
  478.  
  479. #include <stdio.h>  /* printf (), getchar () */
  480. #include <stdlib.h> /* rand ()               */
  481.  
  482. void main (void)
  483. {
  484.   int vector [100]; /* declaración de un vector de 100 elementos int */
  485.   int i, j, interruptor, aux; /* declaración de cuatro variables int */
  486.  
  487.   /* Escritura de la cabecera. */
  488.   printf ("ORDENACION POR EL METODO DE INTERCAMBIO CON TEST DE COMPROBACION:");
  489.  
  490.   /* Relleno aleatorio de los 100 componentes del vector. A cada componente se
  491.      le asigna un valor aleatorio entre 0 y 999. Las componentes del vector van
  492.      desde la 0 hasta la 99 inclusive. */
  493.   for (i = 0; i <= 99; i++)
  494.     vector[i] = rand () % 1000;
  495.  
  496.   /* Escritura en pantalla del vector desordenado. */
  497.   printf ("\n   Vector desordenado:\n");
  498.   for (i = 0; i < 100; i++)
  499.     printf ("%d\t", vector[i]);
  500.  
  501.   /* Ordenación del vector por el método de intercambio directo con test de
  502.      comprobación. */
  503.   i = 0;
  504.   do
  505.     {
  506.       i++;
  507.       interruptor = 0;
  508.       for (j = 99; j >= i; j--)
  509.         if (vector[j-1] > vector[j])
  510.           {
  511.             aux = vector[j-1];
  512.             vector[j-1] = vector[j];
  513.             vector[j] = aux;
  514.             interruptor = 1;
  515.           }
  516.     } while (interruptor == 1 && i <= 99);
  517.  
  518.   /* Escritura en pantalla del vector ordenado. */
  519.   printf ("\n   Vector ordenado:\n");
  520.   for (i = 0; i < 100; i++)
  521.     printf ("%d\t", vector[i]);
  522.  
  523.   /* Espera la pulsación de la tecla RETURN para finalizar el programa. */
  524.   getchar ();
  525. }
  526. ende
  527. begine " ORDENACION POR EL METODO SHELL "
  528. /*
  529.   En este programa se realiza la ordenación creciente de un vector de números
  530.   enteros.
  531.  
  532.   El método de ordenación seguido ha sido el de inserción con incrementos
  533.   decrecientes (también denominado método shell).
  534.  
  535.   Es una mejora del método de inserción directa en la cual se produce un
  536.   acercamiento de los elementos descolocados hacia su posición correcta en
  537.   saltos de mayor longitud. Se repite un mismo proceso con una distancia de
  538.   comparación que inicialmente es la mitad de la longitud del vector y que
  539.   se va reduciendo a la mitad en cada repetición hasta que dicha distancia
  540.   vale 1. Cada pasada termina al detectarse mediante un interruptor la
  541.   ordenación a la distancia correspondiente.
  542.  
  543.   Ejemplo:
  544.  
  545.     Secuencia inicial
  546.     7   5   1   2   4   9   0   3   1
  547.  
  548.     Primer paso. Distancia de comparación 4.
  549.     7   5   1   2   4   9   0   3   1
  550.     └───────────────┘   │   │   │   │
  551.         └───────────────┘   │   │   │
  552.             └───────────────┘   │   │
  553.                 └───────────────┘   │
  554.                     └───────────────┘
  555.     4   5   0   2   1   9   1   3   7
  556.     └───────────────┘   │   │   │   │
  557.         └───────────────┘   │   │   │
  558.             └───────────────┘   │   │
  559.                 └───────────────┘   │
  560.                     └───────────────┘
  561.     1   5   0   2   4   9   1   3   7    ordenados los pares de distancia 4
  562.  
  563.     Segundo paso. Distancia de comparación 2.
  564.     1   5   0   2   4   9   1   3   7
  565.     └───────┘   │   │   │   │   │   │
  566.         └───────┘   │   │   │   │   │
  567.             └───────┘   │   │   │   │
  568.                 └───────┘   │   │   │
  569.                     └───────┘   │   │
  570.                         └───────┘   │
  571.                             └───────┘
  572.     0   2   1   5   1   3   4   9   7
  573.     └───────┘   │   │   │   │   │   │
  574.         └───────┘   │   │   │   │   │
  575.             └───────┘   │   │   │   │
  576.                 └───────┘   │   │   │
  577.                     └───────┘   │   │
  578.                         └───────┘   │
  579.                             └───────┘
  580.     0   2   1   3   1   5   4   9   7    ordenados los pares de distancia 2
  581.  
  582.     Tercer paso. Distancia de comparación 1.
  583.     0   2   1   3   1   5   4   9   7
  584.     └───┘   │   │   │   │   │   │   │
  585.         └───┘   │   │   │   │   │   │
  586.             └───┘   │   │   │   │   │
  587.                 └───┘   │   │   │   │
  588.                     └───┘   │   │   │
  589.                         └───┘   │   │
  590.                             └───┘   │
  591.                                 └───┘
  592.     0   1   2   1   3   4   5   7   9
  593.     └───┘   │   │   │   │   │   │   │
  594.         └───┘   │   │   │   │   │   │
  595.             └───┘   │   │   │   │   │
  596.                 └───┘   │   │   │   │
  597.                     └───┘   │   │   │
  598.                         └───┘   │   │
  599.                             └───┘   │
  600.                                 └───┘
  601.     0   1   1   2   3   4   5   7   9    ordenados los pares de distancia 1
  602.  
  603.   En este programa aparece una función que no se ha visto en teoría: rand ().
  604.   Esta función devuelve un valor aleatorio entre 0 y RAND_MAX. Tanto la función
  605.   rand () como la constante RAND_MAX están declaradas en la librería stdlib.h.
  606.   El valor de RAND_MAX suele ser el valor del mayor número int con signo.
  607. */
  608.  
  609. #include <stdio.h>  /* printf (), getchar () */
  610. #include <stdlib.h> /* rand ()               */
  611.  
  612. void main (void)
  613. {
  614.   int vector [100]; /* declaración de un vector de 100 elementos int */
  615.   int i, division, interruptor, aux; /* declaración de cuatro variables int */
  616.  
  617.   /* Escritura de la cabecera. */
  618.   printf ("ORDENACION POR EL METODO SHELL:");
  619.  
  620.   /* Relleno aleatorio de los 100 componentes del vector. A cada componente se
  621.      le asigna un valor aleatorio entre 0 y 999. Las componentes del vector van
  622.      desde la 0 hasta la 99 inclusive. */
  623.   for (i = 0; i <= 99; i++)
  624.     vector[i] = rand () % 1000;
  625.  
  626.   /* Escritura en pantalla del vector desordenado. */
  627.   printf ("\n   Vector desordenado:\n");
  628.   for (i = 0; i < 100; i++)
  629.     printf ("%d\t", vector[i]);
  630.  
  631.   /* Ordenación del vector por el método shell. */
  632.   division = 99;
  633.   do
  634.     {
  635.       division /= 2;
  636.       do
  637.         {
  638.           interruptor = 0;
  639.           i = 0;
  640.           do
  641.             {
  642.               if (vector[i] > vector[i+division])
  643.                 {
  644.                   aux = vector[i];
  645.                   vector[i] = vector[i+division];
  646.                   vector[i+division] = aux;
  647.                   interruptor = 1;
  648.                 }
  649.         } while (i++ + division < 99);
  650.         } while (interruptor == 1);
  651.     } while (division > 1);
  652.  
  653.   /* Escritura en pantalla del vector ordenado. */
  654.   printf ("\n   Vector ordenado:\n");
  655.   for (i = 0; i < 100; i++)
  656.     printf ("%d\t", vector[i]);
  657.  
  658.   /* Espera la pulsación de la tecla RETURN para finalizar el programa. */
  659.   getchar ();
  660. }
  661. ende
  662. begine " BUSQUEDA LINEAL EN VECTOR DESORDENADO "
  663. /*
  664.   En este programa se realiza una búsqueda lineal en un vector desordenado de
  665.   números enteros.
  666.  
  667.   Método seguido: Se recorre el vector de izquierda a derecha hasta encontrar
  668.   una componente cuyo valor coincida con el buscado o hasta que se acabe el
  669.   vector. En este último caso, el algoritmo debe indicar la no existencia de
  670.   dicho valor. En los casos en que pueda aparecer el valor repetido, el
  671.   algoritmo indicará el de índice menor. Con una pequeña modificación podemos
  672.   obtener las distintas posiciones que ocupa el valor.
  673.  
  674.   Este método es válido tanto para vectores desordenados como ordenados, aunque
  675.   para vectores ordenados veremos otros métodos más eficientes en los ejemplos
  676.   siguientes.
  677. */
  678.  
  679. #include <stdio.h>  /* printf (), getchar () */
  680. #include <stdlib.h> /* rand ()               */
  681.  
  682. void main (void)
  683. {
  684.   int vector [100]; /* declaración de un vector de 100 elementos int */
  685.   int i, x; /* declaración de dos variables int: i se utiliza como índice
  686.                para recorrer el vector y x contendrá el valor a buscar en
  687.                el vector */
  688.  
  689.   /* Escritura de la cabecera. */
  690.   printf ("BUSQUEDA LINEAL EN UN VECTOR DESORDENADO:");
  691.  
  692.   /* Relleno aleatorio de los 100 componentes del vector. A cada componente se
  693.      le asigna un valor aleatorio entre 0 y 999. Las componentes del vector van
  694.      desde la 0 hasta la 99 inclusive. También se asigna un valor aleatorio a
  695.      la variable x que contiene el valor a buscar. */
  696.   for (i = 0; i <= 99; i++)
  697.     vector[i] = rand () % 1000;
  698.   x = rand () % 1000;
  699.  
  700.   /* Escritura en pantalla del vector desordenado y valor a buscar. */
  701.   printf ("\n\nVector:\n");
  702.   for (i = 0; i < 100; i++)
  703.     printf ("%d\t", vector[i]);
  704.   printf ("\nValor a buscar: %d", x);
  705.  
  706.   /* Búsqueda lineal en vector desordenado. */
  707.   for (i = 0; i <= 99 && vector[i] != x; i++)
  708.     ;
  709.  
  710.   /* Escritura en pantalla del vector ordenado. */
  711.   if (i <= 99)
  712.     printf ("\n\nValor %d encontrado en índice %d (recordar que empieza en 0).", x, i);
  713.   else
  714.     printf ("\n\nNo existe el valor %d en el vector.", x);
  715.   /* Espera la pulsación de la tecla RETURN para finalizar el programa. */
  716.   getchar ();
  717. }
  718. ende
  719. begine " BUSQUEDA LINEAL EN VECTOR ORDENADO "
  720. /*
  721.   En este programa se realiza una búsqueda lineal en un vector ordenado de
  722.   números enteros.
  723.  
  724.   Cuando el vector de búsqueda está ordenado se consigue un algoritmo más
  725.   eficiente que el del ejemplo anterior sin más que modificar la codificación
  726.   de terminación en el algoritmo del ejemplo anterior.
  727.  
  728.   La ventana que se obtiene es en el caso de orden ascendente, una vez
  729.   sobrepasado el valor buscado, no es necesario recorrer el resto del vector
  730.   para saber que el valor no existe.
  731. */
  732.  
  733. #include <stdio.h>  /* printf (), getchar () */
  734.  
  735. void main (void)
  736. {
  737.   int vector [100]; /* declaración de un vector de 100 elementos int */
  738.   int i, x; /* declaración de dos variables int: i se utiliza como índice
  739.                para recorrer el vector y x contendrá el valor a buscar en
  740.                el vector */
  741.  
  742.   /* Escritura de la cabecera. */
  743.   printf ("BUSQUEDA LINEAL EN UN VECTOR ORDENADO:");
  744.  
  745.   /* Relleno aleatorio de los 100 componentes del vector. A cada componente se
  746.      le asigna un valor equivalente al doble del índice que ocupan en el vector.
  747.      A la variable x se le asigna arbitrariamente la mitad de lo que vale la
  748.      variable i al salir del bucle. */
  749.   for (i = 0; i <= 99; i++)
  750.     vector[i] = i * 2;
  751.   x = i / 2;
  752.  
  753.   /* Escritura en pantalla del vector ordenado y valor a buscar. */
  754.   printf ("\n\nVector:\n");
  755.   for (i = 0; i < 100; i++)
  756.     printf ("%d\t", vector[i]);
  757.   printf ("\nValor a buscar: %d", x);
  758.  
  759.   /* Búsqueda lineal en vector ordenado. */
  760.   for (i = 0; i <= 99 && vector[i] < x; i++)
  761.     ;
  762.  
  763.   /* Escritura en pantalla del vector ordenado. */
  764.   if (i <= 99)
  765.     printf ("\n\nValor %d encontrado en índice %d (recordar que empieza en 0).", x, i);
  766.   else
  767.     printf ("\n\nNo existe el valor %d en el vector.", x);
  768.   /* Espera la pulsación de la tecla RETURN para finalizar el programa. */
  769.   getchar ();
  770. }
  771. ende
  772. begine " BUSQUEDA BINARIA "
  773. /*
  774.   En este programa se realiza una búsqueda binaria (llamada también dicotómica)
  775.   en un vector ordenado de números enteros.
  776.  
  777.   Este algoritmo es válido exclusivamente para vectores ordenados y consiste
  778.   en comparar en primer lugar con la componente central del vector, y si no
  779.   es igual al valor buscado se reduce el intervalo de búsqueda a la mitad
  780.   derecha o izquierda según donde pueda encontrarse el valor a buscar. El
  781.   algoritmo termina si se encuentra el valor buscado o si el tamaño del
  782.   intervalo de búsqueda queda anulado.
  783.  
  784.   En los casos en que existan repeticiones en el vector, del valor buscado,
  785.   este algoritmo obtendrá uno de ellos aleatoriamente según los lugares que
  786.   ocupen, los cuales necesariamente son consecutivos.
  787. */
  788.  
  789. #include <stdio.h>  /* printf (), getchar () */
  790.  
  791. void main (void)
  792. {
  793.   int vector [100]; /* declaración de un vector de 100 elementos int */
  794.   int x, /* contiene valor a buscar */
  795.       encontrado, /* variable que sólo toma dos valores: cierto (1) o falso (0) */
  796.       i, centro, izquierda, derecha; /* índices de vector */
  797.  
  798.   /* Escritura de la cabecera. */
  799.   printf ("BUSQUEDA BINARIA EN UN VECTOR ORDENADO:");
  800.  
  801.   /* Relleno aleatorio de los 100 componentes del vector. A cada componente se
  802.      le asigna un valor equivalente al doble del índice que ocupan en el vector.
  803.      A la variable x se le asigna arbitrariamente la mitad de lo que vale la
  804.      variable i al salir del bucle. */
  805.   for (i = 0; i <= 99; i++)
  806.     vector[i] = i * 2;
  807.   x = i / 2;
  808.  
  809.   /* Escritura en pantalla del vector ordenado y valor a buscar. */
  810.   printf ("\n\nVector:\n");
  811.   for (i = 0; i < 100; i++)
  812.     printf ("%d\t", vector[i]);
  813.   printf ("\nValor a buscar: %d", x);
  814.  
  815.   /* Búsqueda binaria en vector ordenado. */
  816.   encontrado = 0;
  817.   izquierda = 0;
  818.   derecha = 99;
  819.   while (! encontrado && izquierda <= derecha)
  820.     {
  821.       centro = (izquierda + derecha) / 2;
  822.       if (x < vector[centro])
  823.         derecha = centro - 1;
  824.       else if (x > vector[centro])
  825.         izquierda = centro + 1;
  826.       else
  827.         encontrado = 1;
  828.     }
  829.  
  830.   /* Escritura en pantalla del vector ordenado. */
  831.   if (encontrado)
  832.     printf ("\n\nValor %d encontrado en índice %d (recordar que empieza en 0).", x, centro);
  833.   else
  834.     printf ("\n\nNo existe el valor %d en el vector.", x);
  835.   /* Espera la pulsación de la tecla RETURN para finalizar el programa. */
  836.   getchar ();
  837. }
  838. ende
  839. begint
  840. begine " GENERACION DE 0 Y 1 ALEATORIAMENTE "
  841. /*
  842.   En ejemplos anteriores hemos dicho que la función rand () devuelve un
  843.   número aleatorio entre 0 y RAND_MAX; tanto la función rand () como la
  844.   constante RAND_MAX están definidos en la librería stdlib.h.
  845.  
  846.   Si has ejecutado alguno de los ejemplos anteriores en los que se generan
  847.   números aleatorios, habrás observado que siempre se genera la misma se-
  848.   cuencia de números aleatorios. Esto se debe a que cada número aleatorio
  849.   se genera a partir del anterior; y como el primer número, llamado semilla,
  850.   siempre es el mismo, siempre obtenemos la misma secuencia de números.
  851.  
  852.   En la librería stdlib.h del C estándar tenemos la función srand (). Esta
  853.   función inicializa el generador de números aleatorios con una semilla. Esta
  854.   semilla es un número entero que debemos pasar como parámetro a la función
  855.   srand(). La función srand() no devuelve ningún valor. Por ejemplo: srand (3);
  856.  
  857.   Turbo C dispone de dos funciones más utilizadas que rand () y srand ().
  858.   Dichas funciones son: random () y randomize (); ambas funciones se encuentran
  859.   en stdlib.h.
  860.  
  861.   La función randomize () no acepta ningún parámetro ni devuelve nada. Inicia-
  862.   liza el generador de números aleatorios con un valor aleatorio. Para obtener
  863.   este valor inicial utiliza una función de tiempo que se encuentra en la li-
  864.   brería time.h. Por lo tanto, siempre que utilicemos la función randomize,
  865.   hemos de incluir los ficheros stdlib.h y time.h. Consejo: cuando en un pro-
  866.   grama generemos números aleatorios debemos realizar una llamada a la función
  867.   randomize antes de generar el primero. Ejemplo de llamada: randomize ();
  868.  
  869.   La función random acepta un número n como argumento y devuelve un número
  870.   aleatorio entre 0 y (n-1). Por ejemplo, la instrucción: x = ramdom (10);
  871.   asigna a la variable x un número aleatorio entre 0 y 9.
  872.  
  873.   Con lo que acabamos de hablar hemos completado todo lo que hay en el C con
  874.   respecto a números aleatorios.
  875.  
  876.   El código fuente que hay continuación es un ejemplo de todo lo que hemos
  877.   hablado sobre números aleatorios.
  878. */
  879.  
  880. #include <time.h>   /* randomize () */
  881. #include <stdlib.h> /* randomize (), random () */
  882. #include <stdio.h>  /* printf (), getchar () */
  883.  
  884. void main (void)
  885. {
  886.   const int numero_de_valores_a_generar = 80 * 24, valor_maximo = 2;
  887.   int i;
  888.  
  889.   randomize ();
  890.   for (i = 1; i <= numero_de_valores_a_generar; i++)
  891.     printf ("%d", random (valor_maximo));
  892.   getchar ();
  893. }
  894. ende
  895. endt
  896. end
  897.  
  898. ; LECCION 4
  899. begin
  900. begine " TRANSFORMACION DE NOTA NUMERICA A ALFABETICA "
  901. /*
  902.   Este programa lee una calificación entre 0 y 10 y la transforma en nota
  903.   alfabética según la siguiente tabla:
  904.  
  905.                    Nota numérica   Nota alfabética
  906.                    -------------------------------
  907.                    0 <= NOTA < 3    MUY DEFICIENTE
  908.                    3 <= NOTA < 5    INSUFICIENTE
  909.                    5 <= NOTA < 6    SUFICIENTE
  910.                    7 <= NOTA < 9    NOTABLE
  911.                    9 <= NOTA <= 10  SOBRESALIENTE
  912.   Imprimiendo el resultado.
  913. */
  914.  
  915. #include <stdio.h> // printf (), scanf (), getchar ()
  916.  
  917. void main (void)
  918. {
  919.   int nota;
  920.  
  921.   printf ("Introduce una calificación (0-10): ");
  922.   scanf ("%d", ¬a);
  923.  
  924.   printf ("\nLa notación numérica %d equivale a la alfabética ", nota);
  925.   switch (nota)
  926.     {
  927.       case 0:
  928.       case 1:
  929.       case 2:
  930.         printf ("MUY DEFICIENTE.");
  931.         break;
  932.       case 3:
  933.       case 4:
  934.         printf ("INSUFICIENTE.");
  935.         break;
  936.       case 5:
  937.         printf ("SUFICIENTE.");
  938.         break;
  939.       case 6:
  940.         printf ("BIEN.");
  941.         break;
  942.       case 7:
  943.       case 8:
  944.         printf ("NOTABLE.");
  945.         break;
  946.       case 9:
  947.       case 10:
  948.         printf ("SOBRESALIENTE.");
  949.         break;
  950.       default:
  951.         printf ("NOTA INCORRECTA.");
  952.     }
  953.  
  954.   printf ("\n\nPulsa la tecla RETURN para finalizar.");
  955.   getchar (); /* esta función recoge el RETURN que no coge el scanf () */
  956.   getchar (); /* esta función espera a que el usuario pulse RETURN */
  957. }
  958. ende
  959. begine "CONTADOR DE NUMEROS POSITS, NEGATS Y NULOS DE UNA SERIE"
  960. /*
  961.   Este programa lee una secuencia de números y obtiene e imprime cuántos hay
  962.   positivos, negativos y nulos.
  963. */
  964.  
  965. #include <stdio.h> // printf (), scanf ()
  966. #include <conio.h> // getch ()
  967.  
  968. void main (void)
  969. {
  970.   int n, i, valor, num_de_valores_posit, num_de_valores_negat;
  971.  
  972.   printf ("\nIntroduce el número de valores a leer: ");
  973.   scanf ("%d", &n);
  974.   printf ("\nIntroduce los %d valores: ", n);
  975.  
  976.   for (num_de_valores_posit = num_de_valores_negat = 0, i = 1; i <= n; i++)
  977.     {
  978.       scanf ("%d", &valor);
  979.       if (valor > 0)
  980.         ++num_de_valores_posit;
  981.       else if (valor < 0)
  982.         ++num_de_valores_negat;
  983.     }
  984.  
  985.   printf ("\nNúmero de valores positivos introducidos: %d.",
  986.           num_de_valores_posit);
  987.   printf ("\nNúmero de valores negativos introducidos: %d.",
  988.           num_de_valores_negat);
  989.   printf ("\nNúmero de valores nulos introducidos: %d.",
  990.           n - (num_de_valores_posit + num_de_valores_negat));
  991.  
  992.   printf ("\n\nPulsa cualquier tecla para finalizar.");
  993.   getch ();
  994. }
  995. ende
  996. begine " CALCULO DEL MAYOR Y MENOR NUMERO DE UNA SERIE "
  997. /*
  998.   Este programa lee una secuencia de números terminados con la introducción
  999.   de un 0, y obtiene e imprime el mayor y el menor de ellos.
  1000. */
  1001.  
  1002. #include <stdio.h> // printf (), scanf ()
  1003. #include <conio.h> // getch ()
  1004.  
  1005. void main (void)
  1006. {
  1007.   int valor, valor_mayor, valor_menor;
  1008.   printf ("\nIntroduce una serie de valores terminados en 0: ");
  1009.   scanf ("%d", &valor);
  1010.   if (valor == 0)
  1011.     printf ("\nNo se ha introducido ningún valor.");
  1012.   else
  1013.     {
  1014.       valor_mayor = valor_menor = valor;
  1015.       while (valor != 0)
  1016.         {
  1017.           if (valor > valor_mayor)
  1018.             valor_mayor = valor;
  1019.           if (valor < valor_menor)
  1020.             valor_menor = valor;
  1021.           scanf ("%d", &valor);
  1022.         }
  1023.       printf ("\nMayor valor introducido: %d.\nMenor valor introducido: %d.",
  1024.               valor_mayor, valor_menor);
  1025.     }
  1026.   printf ("\n\nPulsa cualquier tecla para finalizar.");
  1027.   getch ();
  1028. }
  1029. ende
  1030. begine " INVERSION DE UN VECTOR "
  1031. /*
  1032.   El siguiente programa lee 10 números y los escribe en orden inverso.
  1033. */
  1034.  
  1035. #include <stdio.h> // printf (), scanf ()
  1036. #include <conio.h> // getch ()
  1037.  
  1038. void main (void)
  1039. {
  1040.   const numero_de_valores_a_leer = 10;
  1041.   int numeros [numero_de_valores_a_leer];
  1042.   int i;
  1043.  
  1044.   printf ("\nIntroduce %d números: ", numero_de_valores_a_leer);
  1045.   for (i = 0; i <= numero_de_valores_a_leer - 1; i++)
  1046.     scanf ("%d", &numeros[i]);
  1047.  
  1048.   printf ("\nNúmeros leídos en orden inverso:");
  1049.   for (i = numero_de_valores_a_leer - 1; i >= 0; i--)
  1050.     printf (" %d", numeros[i]);
  1051.  
  1052.   printf ("\n\nPulsa cualquier tecla para finalizar.");
  1053.   getch ();
  1054. }
  1055. ende
  1056. begine " CALCULO DE LA MEDIA DE UNA SERIE DE NUMEROS "
  1057. /*
  1058.   Este programa lee por el teclado las notas de los alumnos de una clase y
  1059.   calcula el número de ellas y su media. Para terminar la introducción de
  1060.   notas se introduce una nota negativa.
  1061. */
  1062.  
  1063. #include <stdio.h> // printf (), scanf ()
  1064. #include <conio.h> // getch ()
  1065.  
  1066. void main (void)
  1067. {
  1068.   int nota, numero_de_notas, suma_de_las_notas;
  1069.  
  1070.   printf ("\nPara terminar de introducir notas, escribir nota negativa.\n\n");
  1071.   printf ("Introduce nota 1: ");
  1072.   scanf ("%d", ¬a);
  1073.   numero_de_notas = suma_de_las_notas = 0;
  1074.   while (nota >= 0)
  1075.     {
  1076.       numero_de_notas++;
  1077.       suma_de_las_notas += nota;
  1078.       printf ("Introduce nota %d: ", numero_de_notas + 1);
  1079.       scanf ("%d", ¬a);
  1080.     }
  1081.   if (numero_de_notas == 0)
  1082.     printf ("\n\nNo se ha introducido ninguna nota.");
  1083.   else
  1084.     printf ("\n\nMedia de las notas introducidas: %g",
  1085.             (float) suma_de_las_notas / numero_de_notas);
  1086.   printf ("\n\n\nPulsa cualquier tecla para finalizar.");
  1087.   getch ();
  1088. }
  1089. ende
  1090. begine " VISUALIZADOR DE MEMORIA "
  1091. /*
  1092.   Este programa visualiza 256 bytes de memoria a partir de la dirección
  1093.   de comienzo especificada.
  1094. */
  1095.  
  1096. #include <stdio.h> /* printf (), scanf () */
  1097. #include <conio.h> /* getch () */
  1098.  
  1099. void main (void)
  1100. {
  1101.   register int i;
  1102.   unsigned char ch, *p;
  1103.  
  1104.   printf ("VISUALIZAR MEMORIA.\n\n");
  1105.  
  1106.   printf ("Dirección de comienzo (en hex): ");
  1107.   scanf ("%p%*c", &p);
  1108.  
  1109.   printf ("\n%p: ", p); /* imprime dirección */
  1110.   for (i = 1; i <= 256; i++)
  1111.     {
  1112.       ch = *p;
  1113.       printf ("%02x ", ch); /* visualiza en hexadecimal */
  1114.       p++;
  1115.       if (! (i % 16)) /* cada 16 bytes salta a la línea siguiente */
  1116.         {
  1117.           printf ("\n");
  1118.           if (i != 256)
  1119.             printf ("%p: ", p); /* imprime dirección */
  1120.         }
  1121.     }
  1122.  
  1123.   getch ();
  1124. }
  1125. ende
  1126. end
  1127.  
  1128. ; LECCION 5
  1129. begin
  1130. begine " TORRES DE HANOI "
  1131. /*
  1132.   Este programa mueve n discos de una columna a otra columna utilizando una
  1133.   columna auxiliar. Cada disco es de un tamaño distinto y nunca se puede
  1134.   poner un disco mayor sobre un disco menor. Sólo se puede mover un disco
  1135.   cada vez.
  1136.  
  1137.   Veamos cómo se resolvería en el caso de tres discos:
  1138.  
  1139.   Situación inicial:
  1140.  
  1141.           │             │             │
  1142.         ▄▄│▄▄           │             │
  1143.        ▄▄▄│▄▄▄          │             │
  1144.       ▄▄▄▄│▄▄▄▄         │             │
  1145.      ─────┴─────   ─────┴─────   ─────┴─────
  1146.       columna a     columna b     columna c
  1147.  
  1148.   Movimiento 1:
  1149.  
  1150.           │             │             │
  1151.           │             │             │
  1152.        ▄▄▄│▄▄▄          │             │
  1153.       ▄▄▄▄│▄▄▄▄         │           ▄▄│▄▄
  1154.      ─────┴─────   ─────┴─────   ─────┴─────
  1155.       columna a     columna b     columna c
  1156.  
  1157.   Movimiento 2:
  1158.  
  1159.           │             │             │
  1160.           │             │             │
  1161.           │             │             │
  1162.       ▄▄▄▄│▄▄▄▄      ▄▄▄│▄▄▄        ▄▄│▄▄
  1163.      ─────┴─────   ─────┴─────   ─────┴─────
  1164.       columna a     columna b     columna c
  1165.  
  1166.   Movimiento 3:
  1167.  
  1168.           │             │             │
  1169.           │             │             │
  1170.           │           ▄▄│▄▄           │
  1171.       ▄▄▄▄│▄▄▄▄      ▄▄▄│▄▄▄          │
  1172.      ─────┴─────   ─────┴─────   ─────┴─────
  1173.       columna a     columna b     columna c
  1174.  
  1175.   Movimiento 4:
  1176.  
  1177.           │             │             │
  1178.           │             │             │
  1179.           │           ▄▄│▄▄           │
  1180.           │          ▄▄▄│▄▄▄      ▄▄▄▄│▄▄▄▄
  1181.      ─────┴─────   ─────┴─────   ─────┴─────
  1182.       columna a     columna b     columna c
  1183.  
  1184.   Movimiento 5:
  1185.  
  1186.           │             │             │
  1187.           │             │             │
  1188.           │             │             │
  1189.         ▄▄│▄▄        ▄▄▄│▄▄▄      ▄▄▄▄│▄▄▄▄
  1190.      ─────┴─────   ─────┴─────   ─────┴─────
  1191.       columna a     columna b     columna c
  1192.  
  1193.   Movimiento 6:
  1194.  
  1195.           │             │             │
  1196.           │             │             │
  1197.           │             │          ▄▄▄│▄▄▄
  1198.         ▄▄│▄▄           │         ▄▄▄▄│▄▄▄▄
  1199.      ─────┴─────   ─────┴─────   ─────┴─────
  1200.       columna a     columna b     columna c
  1201.  
  1202.   Movimiento 7:
  1203.  
  1204.           │             │             │
  1205.           │             │           ▄▄│▄▄
  1206.           │             │          ▄▄▄│▄▄▄
  1207.           │             │         ▄▄▄▄│▄▄▄▄
  1208.      ─────┴─────   ─────┴─────   ─────┴─────
  1209.       columna a     columna b     columna c
  1210.  
  1211. Las columnas las designamos por letras y los movimientos serán del tipo:
  1212.   a --> b
  1213.  
  1214. Si el número de discos es mayor que 0:
  1215.   Mover n discos de 'a' a 'c' usando 'b' es equivalente a:
  1216.     mover n-1 discos de 'a' a 'b' usando c
  1217.     mover 1 disco de 'a' a 'c'
  1218.     mover n-1 discos de 'b' a 'c' usando a
  1219.  
  1220. En este programa conviene utilizar la recursividad a la iteratividad ya
  1221. que la lógica es mucho más fácil.
  1222. */
  1223.  
  1224. /* Ficheros a incluir: */
  1225.  
  1226. #include <stdio.h>  /* printf (), puts (), putchar (), scanf () */
  1227. #include <conio.h>  /* getch ()                                 */
  1228. #include <stdlib.h> /* exit ()                                  */
  1229.  
  1230. /* Declaración de funciones: */
  1231.  
  1232. void mover_varios_discos (int n, char p, char q, char r);
  1233. void mover_un_disco (char f, char g);
  1234.  
  1235. /* Definición de funciones: */
  1236.  
  1237. void main (void)
  1238. {
  1239.   int numero_de_discos;
  1240.  
  1241.   puts ("TORRES DE HANOI");
  1242.   printf ("\nIntroduce número de discos a mover: ");
  1243.   scanf ("%d", &numero_de_discos);
  1244.   putchar ('\n');
  1245.  
  1246.   mover_varios_discos (numero_de_discos, 'a', 'c', 'b');
  1247.  
  1248.   printf ("\nPulsa cualquier tecla para finalizar.");
  1249.   getch ();
  1250. }
  1251.  
  1252. void mover_varios_discos (int numero_de_discos, char columna_origen,
  1253.                           char columna_destino, char columna_auxiliar)
  1254. {
  1255.   if (numero_de_discos > 0)
  1256.     {
  1257.       mover_varios_discos (numero_de_discos-1, columna_origen, columna_auxiliar,
  1258.                            columna_destino);
  1259.       mover_un_disco (columna_origen, columna_destino);
  1260.       mover_varios_discos (numero_de_discos-1, columna_auxiliar, columna_destino,
  1261.                            columna_origen);
  1262.     }
  1263. }
  1264.  
  1265. void mover_un_disco (char columna_origen, char columna_destino)
  1266. {
  1267.   static unsigned int numero_de_movimiento = 0;
  1268.   /* cada 24 movimientos se para la escritura */
  1269.   if (++numero_de_movimiento % 24 == 0)
  1270.     {
  1271.       printf ("Pulsa la tecla ESCAPE para salir o cualquier otra tecla para continuar.");
  1272.       /* el código ASCII 27 corresponde a la tecla ESCAPE */
  1273.       if (getch () == 27)
  1274.         exit (0);
  1275.       /* con estas tres funciones se borra el mensaje anterior */
  1276.       putchar ('\r');
  1277.       printf ("                                                                       ");
  1278.       putchar ('\r');
  1279.     }
  1280.   printf ("Movimiento %2u: %c --> %c\n", numero_de_movimiento, columna_origen,
  1281.           columna_destino);
  1282. }
  1283. ende
  1284. begine " PERMUTACIONES "
  1285. /*
  1286.   Este programa lee los elementos de un vector y escribe todas las
  1287.   permutaciones (combinaciones sin repetición) posibles de los
  1288.   elementos introducidos en el vector.
  1289.  
  1290.   El número de permutaciones de n elementos está determinado por el
  1291.   factorial de n.
  1292.  
  1293.   Observaciones sobre el programa:
  1294.  
  1295.     1) En el primer elemento del vector (índice 0) se encuentra el
  1296.     número de elementos a permutar. Así pues, los n elementos a
  1297.     permutar están en entre los índices 1 y n el vector.
  1298.  
  1299.     2) En algunos sitios del programa aparece la expresión:
  1300.         &vector[i]
  1301.     que puede parecer a simple un poco rara para el que no está
  1302.     acostumbrado al C. Expliquémosla: Al ser cada elmento del
  1303.     vector de tipo int, vector[i] se puede considerar como una
  1304.     variable de tipo int, y por lo tanto, podemos hacer con la
  1305.     expresión vector[i] lo que con cualquier variable de tipo
  1306.     entero, y no de estas operaciones es obtener su dirección.
  1307.     Hemos dicho varias veces que el nombre del vector es un puntero
  1308.     al primer elemento, así la siguiente expresión sería cierta:
  1309.     vector == &vector[0], es decir, que vector y &vector[0] es
  1310.     equivalente.
  1311.  
  1312.     3) Los nombres de las variables punteros, por convención, suelen
  1313.     empezar con p; por este motivo he llamado a los parámetros de la
  1314.     función intercambiar p1 y p2.
  1315. */
  1316.  
  1317. /* Ficheros a incluir: */
  1318.  
  1319. #include <stdio.h>  /* printf (), puts (), putchar (), scanf () */
  1320. #include <conio.h>  /* getch ()                                 */
  1321. #include <stdlib.h> /* exit ()                                  */
  1322.  
  1323. /* Macros: */
  1324.  
  1325. #define NUMERO_MAXIMO_ELEMENTOS_VECTOR 100
  1326. #define en(x,x1,x2) ((x) >= (x1) && (x) <= (x2))
  1327.  
  1328. /* Declaración de las funciones: */
  1329.  
  1330. void rellenar_vector (int vect[]);
  1331. void escribir_vector (int vect[]);
  1332. void permutar (int v[], int m);
  1333. void intercambiar (int *p1, int *p2);
  1334.  
  1335. /* Definición de las funciones: */
  1336.  
  1337. void main (void)
  1338. {
  1339.   int vector [NUMERO_MAXIMO_ELEMENTOS_VECTOR+1]; /* +1 porque el primer
  1340.              elemento contiene el número de elementos a permutar */
  1341.  
  1342.   puts ("PERMUTACIONES");
  1343.   rellenar_vector (vector);
  1344.  
  1345.   permutar (vector, vector[0]);
  1346.  
  1347.   printf ("\nPulsa cualquier tecla para finalizar.");
  1348.   getch ();
  1349. }
  1350.  
  1351. void rellenar_vector (int vect[])
  1352. {
  1353.   register int i;
  1354.   do
  1355.     {
  1356.       printf ("\nIntroduce número de elementos de vector (1-%d): ",
  1357.               NUMERO_MAXIMO_ELEMENTOS_VECTOR);
  1358.       scanf ("%d", &vect[0]);
  1359.     } while (! en (vect[0], 1, NUMERO_MAXIMO_ELEMENTOS_VECTOR));
  1360.   printf ("\nIntroduce los %d elementos a permutar: ", vect[0]);
  1361.   for (i = 1; i <= vect[0]; i++)
  1362.     scanf ("%d", &vect[i]);
  1363.   putchar ('\n');
  1364. }
  1365.  
  1366. void escribir_vector (int vect[])
  1367. {
  1368.   static unsigned int numero_de_permutacion = 0;
  1369.   register int i;
  1370.  
  1371.   /* cada 24 movimientos se para la escritura */
  1372.   if (++numero_de_permutacion % 24 == 0)
  1373.     {
  1374.       printf ("Pulsa la tecla ESCAPE para salir o cualquier otra tecla para continuar.");
  1375.       /* el código ASCII 27 corresponde a la tecla ESCAPE */
  1376.       if (getch () == 27)
  1377.         exit (0);
  1378.       /* con estas tres funciones se borra el mensaje anterior */
  1379.       putchar ('\r');
  1380.       printf ("                                                                       ");
  1381.       putchar ('\r');
  1382.     }
  1383.   printf ("Permutación %2u: ", numero_de_permutacion);
  1384.   for (i = 1; i <= vect[0]; i++)
  1385.     printf ("%d ", vect[i]);
  1386.   putchar ('\n');
  1387. }
  1388.  
  1389. void permutar (int v[], int m)
  1390. {
  1391.   register int i;
  1392.   if (m > 1)
  1393.     for (i = 1; i <= m; i++)
  1394.       {
  1395.         intercambiar (&v[i], &v[m]);
  1396.         permutar (v, m-1);
  1397.         intercambiar (&v[i], &v[m]);
  1398.       }
  1399.   else
  1400.     escribir_vector (v);
  1401. }
  1402.  
  1403. void intercambiar (int *p1, int *p2)
  1404. {
  1405.   int aux;
  1406.   aux = *p1;
  1407.   *p1 = *p2;
  1408.   *p2 = aux;
  1409. }
  1410. ende
  1411. begine " ORDENACION POR EL METODO DE ORDENACION RAPIDA "
  1412. /*
  1413.   En este programa se realiza la ordenación creciente de un vector de números
  1414.   enteros.
  1415.  
  1416.   El método de ordenación seguido ha sido el de ordenación rápida.
  1417.  
  1418.   La ordenación rápida, inventada y nombrada por C.A.R. Hoare, es mejor que
  1419.   todas las vistas en los ejemplos de la lección 4 y está generalmente con-
  1420.   siderada como el mejor algoritmo de ordenación disponible actualmente. Está
  1421.   basada en la ordenación por el método de intercambio.
  1422.  
  1423.   La ordenación rápida se basa en la idea de las particiones. El procedimiento
  1424.   general es seleccionar un valor llamado comparando y entonces dividir el
  1425.   array en dos partes. En un lado todos los elementos mayores o iguales al
  1426.   valor de partición y en otro todos los elementos menores que el valor. Este
  1427.   proceso se repite en cada parte restante hasta que el array está ordenado.
  1428.  
  1429.   Como se puede ver, este proceso es esencialmente recursivo por naturaleza y,
  1430.   de hecho, las implementaciones más claras de la ordenación rápida es por al-
  1431.   goritmos recursivos.
  1432.  
  1433.   La selección del valor comparado se puede obtener de dos formas. Se puede
  1434.   seleccionar aleatoriamente o haciendo la media de un pequeño conjunto de
  1435.   valores tomados del array. Para una ordenación óptima es mejor seleccionar
  1436.   un valor que esté precisamente en medio del rango de valores. Sin embargo,
  1437.   esto no es fácil de hacer en la mayoría de los conjuntos de datos. En el
  1438.   caso peor, el valor escogido está en un extremo. Incluso en este, la orde-
  1439.   nación rápida todavía funciona bien. La versión de la ordenación rápida que
  1440.   sigue selecciona el elemento mitad del array. Aunque no siempre será una
  1441.   buena elección, la ordenación sigue funcionanado correctamente.
  1442.  
  1443.   Ejemplo:
  1444.  
  1445.     Secuencia inicial   8   2   5   3   9
  1446.     Elemento comparando: 5
  1447.     Primer paso         3   2   5   8   9
  1448.     Ahora se ordena con el mismo procedimiento los vectores '3 2' y '8 9'
  1449.  
  1450.   En este programa aparece una función que no se ha visto en teoría: rand ().
  1451.   Esta función devuelve un valor aleatorio entre 0 y RAND_MAX. Tanto la función
  1452.   rand () como la constante RAND_MAX están declaradas en la librería stdlib.h.
  1453.   El valor de RAND_MAX suele ser el valor del mayor número int con signo. Esta
  1454.   función ya se vio en los ejemplos de la lección 4.
  1455. */
  1456.  
  1457. #include <stdio.h>  /* printf () */
  1458. #include <stdlib.h> /* rand ()   */
  1459. #include <conio.h>  /* getch ()  */
  1460.  
  1461. #define NUM_ELEMENTOS_VECTOR 100
  1462.  
  1463. void ordenar (int vect[], int ind_izq, int ind_der);
  1464.  
  1465. void main (void)
  1466. {
  1467.   int vector [NUM_ELEMENTOS_VECTOR]; /* declaración de un vector de elementos int */
  1468.   register int i; /* declaración de una variable resistro de tipo entera */
  1469.  
  1470.   /* Escritura de la cabecera. */
  1471.   printf ("ORDENACION DE UN VECTOR DESORDENADO POR EL METODO DE ORDENACION RAPIDA:");
  1472.  
  1473.   /* Relleno aleatorio de los 100 componentes del vector. A cada componente se
  1474.      le asigna un valor aleatorio entre 0 y 999. Las componentes del vector van
  1475.      desde la 0 hasta la NUM_ELEMENTOS_VECTOR-1 inclusive. */
  1476.   for (i = 0; i < NUM_ELEMENTOS_VECTOR; i++)
  1477.     vector[i] = rand () % 1000;
  1478.  
  1479.   /* Escritura en pantalla del vector desordenado. */
  1480.   printf ("\n   Vector desordenado:\n");
  1481.   for (i = 0; i < NUM_ELEMENTOS_VECTOR; i++)
  1482.     printf ("%d\t", vector[i]);
  1483.  
  1484.   /* Ordenación del vector por el método de inserción directa. */
  1485.   ordenar (vector, 0, NUM_ELEMENTOS_VECTOR-1);
  1486.  
  1487.   /* Escritura en pantalla del vector ordenado. */
  1488.   printf ("\n   Vector ordenado:\n");
  1489.   for (i = 0; i < NUM_ELEMENTOS_VECTOR; i++)
  1490.     printf ("%d\t", vector[i]);
  1491.  
  1492.   /* Espera la pulsación de cualquier tecla para finalizar el programa. */
  1493.   getch ();
  1494. }
  1495.  
  1496. void ordenar (int vect[], int ind_izq, int ind_der)
  1497. { /* vect: vector, ind_izq: índice izquierdo, ind_der: índice derecho */
  1498.   register int i, j; /* variables índice del vector */
  1499.   int elem; /* contiene un elemento del vector */
  1500.  
  1501.   i = ind_izq;
  1502.   j = ind_der;
  1503.   elem = vect[(ind_izq+ind_der)/2];
  1504.  
  1505.   do
  1506.     {
  1507.       while (vect[i] < elem && j < ind_der) /* recorrido del vector hacia la derecha */
  1508.         i++;
  1509.       while (elem < vect[j] && j > ind_izq) /* recorrido del vector hacia la izquierda */
  1510.         j--;
  1511.       if (i <= j) /* intercambiar */
  1512.         {
  1513.           int aux; /* variable auxiliar */
  1514.           aux = vect[i];
  1515.           vect[i] = vect[j];
  1516.           vect[j] = aux;
  1517.           i++;
  1518.           j--;
  1519.         }
  1520.     } while (i <= j);
  1521.   if (ind_izq < j)
  1522.     ordenar (vect, ind_izq, j);
  1523.   if (i < ind_der)
  1524.     ordenar (vect, i, ind_der);
  1525. }
  1526. ende
  1527. begint
  1528. begine " DIBUJO DE UN GRAFICO "
  1529. /*
  1530.   Este programa dibuja un gráfico de una forma recursiva. Está implementado
  1531.   en Turbo C.
  1532.  
  1533.   Ejecutar el programa antes de leer la explicación sobre el mismo para
  1534.   enteder la explicación mejor.
  1535.  
  1536.   La atractiva forma gráfica que muestra en la ejecución del programa está
  1537.   formada por la superposición de cinco curvas. Estas siguen un esquema
  1538.   regular y sugieren que deben poder dibujarse por un «plotter» controlado
  1539.   por computador. El objetivo a conseguir es descubrir el esquema recursivo
  1540.   con el que debe construirse el programa que haga el dibujo. Por inspección
  1541.   se observa que tres de las curvas superpuestas tienen la forma indicada
  1542.   en los dibujos siguientes; se designan por H1, H2 y H3.
  1543.  
  1544.     Curva de Hilbert de orden 1:
  1545.  
  1546.     ┌───────
  1547.     │
  1548.     │
  1549.     └───────
  1550.  
  1551.     Curva de Hilbert de orden 2:
  1552.  
  1553.     ┌───────┐
  1554.     │       │   │
  1555.     └───┐   └───┘
  1556.         │
  1557.     ┌───┘   ┌───┐
  1558.     │       │   │
  1559.     └───────┘
  1560.  
  1561.     Curva de Hilbert de orden 3:
  1562.  
  1563.     ┌───┐ ┌───┐ ┌─
  1564.     └─┐ └─┘ ┌─┘ └─┐
  1565.     ┌─┘ ┌─┐ │ ┌─┐ │
  1566.     └───┘ │ └─┘ └─┘
  1567.     ┌───┐ │ ┌─┐ ┌─┐
  1568.     └─┐ └─┘ │ └─┘ │
  1569.     ┌─┘ ┌─┐ └─┐ ┌─┘
  1570.     └───┘ └───┘ └─
  1571.  
  1572.   Las figuras muestran que H(i+1) se obtiene por composición de cuatro curvas
  1573.   de tipo H(i) de tamaño mitad, giradas apropiadamente, y unidas por tres
  1574.   líneas. Obsérvese que H(1) puede considerarse formada por cuatro curvas de
  1575.   un tipo vacío H(0) conectadas por tres rectas. H(i) se denomina curva de
  1576.   Hilbert de orden i, en honor a su inventor D. Hilbert (1891).
  1577.  
  1578.   Supóngase que las herramientas básicas de que se dispone para dibujar son
  1579.   dos variables de coordenadas x e y, un procedimiento setplot (situar la
  1580.   pluma del «plotter» en las coordenadas x e y) y un procedimiento plot (que
  1581.   mueve la pluma de dibujo desde la situación actual a la posición indicada
  1582.   por x e y).
  1583.  
  1584.   Como cada curva H(i) está formada por cuatro copias de tamaño mitad de la
  1585.   curva H(i-1) es lógico expresar el procedimiento de dibujar H(i) como
  1586.   compuesto de cuatro partes, cada una dibujando una H(i-1) del tamaño apro-
  1587.   piado, convenientemente girada. Si se denomina cada parte, respectivamente,
  1588.   por A, B, C y D, y las rutinas que dibujan las correspondientes líneas de
  1589.   interconexión se representan por flechas apuntando en la dirección corres-
  1590.   pondiente aparece el siguiente esquema recursivo:
  1591.  
  1592.             ┌─
  1593.             └─>  A:  D <- A   A -> B
  1594.  
  1595.             ┌─┐
  1596.             │   B:  C   B -> B   A
  1597.  
  1598.             <─┐
  1599.              ─┘  C:  B -> C   C <- D
  1600.  
  1601.              │
  1602.             └─┘  D:  A   D <- D   C
  1603.  
  1604.   Si se designa la línea unitaria por h, el procedimiento correspondiente al
  1605.   esquema A se expresa inmediatamente uilizando activaciones recursivas de los
  1606.   procedimientos designados análogamente por B y D y del propio A.
  1607.  
  1608.     procedimiento A (i es entero)
  1609.       si i > 0 entonces
  1610.         D (i-1); x = x - h; plot;
  1611.         A (i-1); y = y - h; plot;
  1612.         A (i-1); x = x + h; plot;
  1613.         B (i-1);
  1614.       finsi
  1615.     finprocedimiento
  1616.  
  1617.   Este procedimiento se inicia por el programa principal una vez por cada curva
  1618.   de Hilbert a superponer. El programa principal determina el punto inicial de
  1619.   la curva, o sea, los valores iniciales de x e y, y el incremento unitario h. Se
  1620.   llama h0 al ancho total de la página, que debe ser h0 = (2 elevado a k) para
  1621.   algún k ≥ n. El programa completo dibuja las n curvas de Hilbert H1, ... Hn.
  1622.  
  1623.   Descripción de las características gráficas utilizadas de Turbo C (concreta-
  1624.   mente de la versión Borland C++ 2.0).
  1625.  
  1626.   Para trabajar con gráfico es necesario incluir el fichero <graphics.h>.
  1627.  
  1628.   En Turbo C, siempre que se vaya a utilizar el modo gráfico, es necesario
  1629.   inicializarlo y cerrarlo.
  1630.  
  1631.   La inicialización se hace con la función initgraph(). El prototipo de esta
  1632.   función es:
  1633.  
  1634.     void initgraph (int *graphdriver, int *graphmode, char *pathtodriver);
  1635.  
  1636.   donde graphdriver debe contener el dispositivo gráfico; los valores que
  1637.   puede tomar son:
  1638.  
  1639.     CGA           MCGA
  1640.     EGA           EGA64
  1641.     EGAMONO       IBM8514
  1642.     HERCMONO      ATT400
  1643.     VGA           PC3270
  1644.     DETECT (autodetección de la tarjeta gráfica)
  1645.  
  1646.   En graphmode se guarda el modo gráfico para cada dispositivo gráfico; los
  1647.   valores que puede tomar son:
  1648.  
  1649.     ════════════╤═════════════╤══════════════
  1650.      CGAC0      │  320 x 200  │  paleta 0
  1651.      CGAC1      │  320 x 200  │  paleta 1
  1652.      CGAC2      │  320 x 200  │  paleta 2
  1653.      CGAC3      │  320 x 200  │  paleta 3
  1654.      CGAHI      │  640 x 200  │  2 colores
  1655.                 │             │
  1656.      MCGAC0     │  320 x 200  │  paleta 0
  1657.      MCGAC1     │  320 x 200  │  paleta 1
  1658.      MCGAC2     │  320 x 200  │  paleta 2
  1659.      MCGAC3     │  320 x 200  │  paleta 3
  1660.      MCGAMED    │  640 x 200  │  2 colores
  1661.      MCGAHI     │  640 x 480  │  2 colores
  1662.                 │             │
  1663.      EGALO      │  640 x 200  │ 16 colores
  1664.      EGAHI      │  640 x 350  │ 16 colores
  1665.      EGA64LO    │  640 x 200  │ 16 colores
  1666.      EGA64HI    │  640 x 350  │  4 colores
  1667.                 │             │
  1668.      EGAMONOHI  │  640 x 350  │  2 colores
  1669.      HERCMONOHI │  720 x 348  │  2 colores
  1670.                 │             │
  1671.      ATT400C0   │  320 x 200  │  paleta 0
  1672.      ATT400C1   │  320 x 200  │  paleta 1
  1673.      ATT400C2   │  320 x 200  │  paleta 2
  1674.      ATT400C3   │  320 x 200  │  paleta 3
  1675.      ATT400MED  │  640 x 200  │  2 colores
  1676.      ATT400HI   │  640 x 400  │  2 colores
  1677.                 │             │
  1678.      VGALO      │  640 x 200  │ 16 colores
  1679.      VGAMED     │  640 x 350  │ 16 colores
  1680.      VGAHI      │  640 x 480  │ 16 colores
  1681.                 │             │
  1682.      PC3270HI   │  720 x 350  │  2 colores
  1683.      IBM8514LO  │  640 x 480  │256 colores
  1684.      IBM8514HI  │ 1024 x 768  │256 colores
  1685.  
  1686.   En pathtodriver debe estar el path del directorio donde están los ficheros
  1687.   con extensión .BGI. Su sintaxis es:
  1688.  
  1689.     "..\\bgi\\drivers"
  1690.  
  1691.   El modo gráfico se cierra con la función closegraph() cuyo prototipo es:
  1692.  
  1693.     void closegraph (void);
  1694.  
  1695.   La esquina izquierda superior de la pantalla gráfica tiene coordenadas: 0, 0.
  1696.  
  1697.   Funciones gráficas utilizadas en este programa:
  1698.  
  1699.     1) void lineto (int x, int y);
  1700.  
  1701.       Dibuja una línea desde la posición actual del cursor gráfico hasta (x,y).
  1702.  
  1703.     2) void moveto (int x, int y);
  1704.  
  1705.       Mueve el cursor gráfico a (x,y).
  1706.  
  1707.     3) int graphresult (void);
  1708.  
  1709.       Devuelve un código de error para la última operación gráfica no exitosa
  1710.       o grOk si la última operación gráfica ha tenido éxito.
  1711.  
  1712.       Los códigos de error devueltos por graphresult() son:
  1713.  
  1714.         grOk                grNoInitGraph
  1715.         grNotDetected       grFileNotFound
  1716.         grInvalidDriver     grNoLoadMem
  1717.         grNoScanMem         grNoFloodMem
  1718.         grFontNotFound      grNoFontMem
  1719.         grInvalidMode       grError
  1720.         grIOerror           grInvalidFont
  1721.         grInvalidFontNum    grInvalidDeviceNum
  1722.         grInvalidVersion
  1723.  
  1724.     4) char *grapherrormsg (int errorcode);
  1725.  
  1726.       Devuelve un puntero a un string con el mensaje de error.
  1727.  
  1728.     5) void setviewport (int izq, int ar, int der, int ab, int clip);
  1729.  
  1730.       Crea un viewport para la salida gráfica.
  1731.       Un viewport es una ventana de trabajo en la pantalla gráfica de tal
  1732.       modo que la coordenada (0,0) corresponde a la esquina izquierda superior
  1733.       del viewport y no de la pantalla.
  1734.       El parámetro clip especifica si los valores fuera del viewport son
  1735.       recortados.
  1736.  
  1737.   Las características gráficas de Turbo C se estudiará en lecciones posteriores.
  1738.   Aquí sólo hemos dado los conceptos básicos sobre ellas para comprender cómo
  1739.   está hecho el programa.
  1740. */
  1741.  
  1742. #include <graphics.h> /* lineto (), moveto (), setviewport (), initgrah (),
  1743.                          graphresult (), grOk, getmaxx (), getmaxy ()      */
  1744. #include <stdio.h>    /* printf ()                                         */
  1745. #include <conio.h>    /* geth ()                                           */
  1746. #include <stdlib.h>   /* exit (), EXIT_FAILURE                             */
  1747.  
  1748. int h0, h, x, y;
  1749.  
  1750. void a (int), b (int), c (int), d (int);
  1751. void inicializar_grafico (void);
  1752.  
  1753. #define empezargraf  inicializar_grafico ();
  1754. #define plot         lineto (x, y)
  1755. #define setplot      moveto (x, y);
  1756. #define terminargraf closegraph ();
  1757.  
  1758. void main (void)
  1759. {
  1760.   const int n = 4;
  1761.   int i, x0, y0;
  1762.  
  1763.   empezargraf;
  1764.  
  1765.   i = 0;
  1766.   h = h0;
  1767.   x0 = h / 2;
  1768.   y0 = x0;
  1769.  
  1770.   do
  1771.     {
  1772.       i++;
  1773.       h /= 2;
  1774.       x0 += (h/2);
  1775.       y0 += (h/2);
  1776.       x = x0;
  1777.       y = y0;
  1778.       setplot;
  1779.       a (i);
  1780.     } while (i != n);
  1781.  
  1782.    getch ();
  1783.    terminargraf;
  1784. }
  1785.  
  1786. void inicializar_grafico (void)
  1787. {
  1788.   int gdriver = DETECT, gmode;
  1789.   int vpx1, vpy1, vpx2, vpy2;
  1790.   int errorcode;
  1791.  
  1792.   initgraph (&gdriver, &gmode, "");
  1793.   errorcode = graphresult ();
  1794.  
  1795.   if (errorcode != grOk)
  1796.     {
  1797.       printf ("Error Gráfico: %s\n", grapherrormsg (errorcode));
  1798.       getch ();
  1799.       exit (EXIT_FAILURE);
  1800.     }
  1801.  
  1802.   h0 = getmaxx () + 1 >= 640 && getmaxy () + 1 >= 480 ? 512 : 128;
  1803.  
  1804.   /* calcula las coordenadas de viewport para crear un viewport en el centro
  1805.      de la pantalla de altura y anchura h0 si puede; esto se hace para que
  1806.      el gráfico salga centrado en la pantalla */
  1807.   vpx1 = (getmaxx () + 1 - h0) / 2 + 1;
  1808.   vpy1 = getmaxy () + 1 <= h0 ? 0 : (getmaxy () + 1 - h0) / 2 + 1;
  1809.   vpx2 = vpx1 + h0 - 1;
  1810.   vpy2 = getmaxy () + 1 <= h0 ? getmaxy () : vpy1 + h0 - 1;
  1811.  
  1812.   setviewport (vpx1, vpy1, vpx2, vpy2, 0);
  1813. }
  1814.  
  1815. void a (int i)
  1816. {
  1817.   if (i > 0)
  1818.     {
  1819.       d (i-1); x -= h; plot;
  1820.       a (i-1); y -= h; plot;
  1821.       a (i-1); x += h; plot;
  1822.       b (i-1);
  1823.     }
  1824. }
  1825.  
  1826. void b (int i)
  1827. {
  1828.   if (i > 0)
  1829.     {
  1830.       c (i-1); y += h; plot;
  1831.       b (i-1); x += h; plot;
  1832.       b (i-1); y -= h; plot;
  1833.       a (i-1);
  1834.     }
  1835. }
  1836.  
  1837. void c (int i)
  1838. {
  1839.   if (i > 0)
  1840.     {
  1841.       b (i-1); x += h; plot;
  1842.       c (i-1); y += h; plot;
  1843.       c (i-1); x -= h; plot;
  1844.       d (i-1);
  1845.     }
  1846. }
  1847.  
  1848. void d (int i)
  1849. {
  1850.   if (i > 0)
  1851.     {
  1852.       a (i-1); y -= h; plot;
  1853.       d (i-1); x -= h; plot;
  1854.       d (i-1); y += h; plot;
  1855.       c (i-1);
  1856.     }
  1857. }
  1858. ende
  1859. begine " DIBUJO DE UN GRAFICO 2 "
  1860. /*
  1861.   Este programa dibuja un gráfico de una forma recursiva. Está implementado
  1862.   en Turbo C.
  1863.  
  1864.   Ejecutar el programa antes de leer la explicación sobre el mismo para
  1865.   enteder la explicación mejor. Leer además también la explicación del
  1866.   ejemplo anterior antes que ésta.
  1867.  
  1868.   Este ejemplo es semejante al anterior aunque algo más complejo y más
  1869.   sofisticado desde el punto de vista estético. Esta forma se obtiene
  1870.   también por superposición de varias curvas. S(i) se denomina la curva
  1871.   de Sierpinski de orden i. ¿Cuál es el esquema recursivo? Se siente uno
  1872.   tentado de identificar la «hoja» S(1) como bloque básico de construcción,
  1873.   posiblemente quitándole un borde. Pero esto no conduce a una solución. La
  1874.   diferencia principal entre las curvas de Hilbert y Sierpinski es que estas
  1875.   últimas son cerradas (sin discontinuidades). Esto implica que el esquema
  1876.   básico de recursión debe ser una curva abierta y que las cuatro partes
  1877.   deben estar conectadas por líneas no pertenecientes al propio esquema
  1878.   recursivo. Realmente, estas líneas están formadas por cuatro rectas en
  1879.   las cuatro esquinas de los bordes. Pueden considerarse como pertenecientes
  1880.   a una curva inicial no vacía, S(0), que es un cuadrado apoyado en un vértice.
  1881.  
  1882.   A partir de esto, el esquema recursivo se establece inmediatamente. Las
  1883.   formas básicas se denominan nuevamente A, B, C y D y las líneas de conexión
  1884.   se dibujan explícitamente. Obsérvese que los cuatro esquemas recursivos son
  1885.   idénticos realmente, con la única diferencia de que están girados 90º.
  1886.  
  1887.             S: A diagonal_hacia_abajo_de_izq_a_der
  1888.                B diagonal_hacia_abajo_de_der_a_izq
  1889.                C diagonal_hacia_arriba_de_der_a_izq
  1890.                D diagonal_hacia_arriba_de_izq_a_der
  1891.  
  1892.   y los esquemas recursivos son
  1893.  
  1894.             A: A diagonal_hacia_abajo_de_izq_a_der
  1895.                B linea_horizontal_doble_de_izq_a_der
  1896.                D diagonal_hacia_arriba_de_izq_a_der
  1897.                A
  1898.  
  1899.             B: B diagonal_hacia_abajo_de_der_a_izq
  1900.                C linea_horizontal_doble_de_ar_a_ab
  1901.                A diagonal_hacia_abajo_de_izq_a_der
  1902.                B
  1903.  
  1904.             C: C diagonal_hacia_arriba_de_der_a_izq
  1905.                D linea_horizontal_doble_de_der_a_izq
  1906.                B diagonal_hacia_abajo_de_der_a_izq
  1907.                C
  1908.  
  1909.             D: D diagonal_hacia_arriba_de_izq_a_der
  1910.                A linea_horizontal_doble_de_ab_a_ar
  1911.                C diagonal_hacia_arriba_de_der_a_izq
  1912.                D
  1913.  
  1914.   Si se usan los mismos procedimientos básicos para la operaciones de dibujo
  1915.   que en el ejemplo de las curvas de Hilbert, el anterior esquema recursivo
  1916.   se transforma sin dificultad en un algoritmo (directa e indirectamente)
  1917.   recursivo.
  1918.  
  1919.     procedimiento A (i es estero)
  1920.       si i > 0 entonces
  1921.         A (i-1); x = x + h; y = y - h; plot;
  1922.         B (i-1); x = x + 2 * h; plot;
  1923.         D (i-1); x = x + h; y = y + h; plot;
  1924.         A (i-1);
  1925.       finsi
  1926.     finprocedimiento
  1927.  
  1928.   Pueden obtenerse en forma análoga procedimientos correspondientes a los
  1929.   esquemas B, C y D.
  1930.  
  1931.   Las características gráficas utilizadas en este programa están explicadas
  1932.   en el ejemplo anterior ya que los dos ejemplos son similares.
  1933. */
  1934.  
  1935. #include <graphics.h> /* lineto (), moveto (), setviewport (), initgrah (),
  1936.                          graphresult (), grOk, getmaxx (), getmaxy ()      */
  1937. #include <stdio.h>    /* printf ()                                         */
  1938. #include <conio.h>    /* geth ()                                           */
  1939. #include <stdlib.h>   /* exit (), EXIT_FAILURE                             */
  1940.  
  1941. int h0, h, x, y;
  1942.  
  1943. void a (int), b (int), c (int), d (int);
  1944.  
  1945. void inicializar_grafico (void);
  1946.  
  1947. #define empezargraf  inicializar_grafico ();
  1948. #define plot         lineto (x, y)
  1949. #define setplot      moveto (x, y);
  1950. #define terminargraf closegraph ();
  1951.  
  1952. void main (void)
  1953. {
  1954.   const int n = 7;
  1955.   int i, x0, y0;
  1956.  
  1957.   empezargraf;
  1958.  
  1959.   i = 0;
  1960.   h = h0 / 4;
  1961.   x0 = 2 * h;
  1962.   y0 = 3 * h;
  1963.  
  1964.   do
  1965.     {
  1966.       i++;
  1967.       x0 -= h;
  1968.       h /= 2;
  1969.       y0 += h;
  1970.       x = x0;
  1971.       y = y0;
  1972.       setplot;
  1973.       A (i); x += h; y -= h; plot;
  1974.       B (i); x -= h; y -= h; plot;
  1975.       C (i); x -= h; y += h; plot;
  1976.       D (i); x += h; y += h; plot;
  1977.       /* 27 es el carácter ASCII de la tecla ESCAPE */
  1978.     } while (getch () != 27 && i != n);
  1979.  
  1980.    terminargraf;
  1981. }
  1982.  
  1983. void inicializar_grafico (void)
  1984. {
  1985.   int gdriver = DETECT, gmode;
  1986.   int vpx1, vpy1, vpx2, vpy2;
  1987.   int errorcode;
  1988.  
  1989.   initgraph (&gdriver, &gmode, "");
  1990.   errorcode = graphresult ();
  1991.  
  1992.   if (errorcode != grOk)
  1993.     {
  1994.       printf ("Error Gráfico: %s\n", grapherrormsg (errorcode));
  1995.       getch ();
  1996.       exit (EXIT_FAILURE);
  1997.     }
  1998.  
  1999.   h0 = getmaxx () + 1 >= 640 && getmaxy () + 1 >= 480 ? 512 : 128;
  2000.  
  2001.   /* calcula las coordenadas de viewport para crear un viewport en el centro
  2002.      de la pantalla de altura y anchura h0 si puede; esto se hace para que
  2003.      el gráfico salga centrado en la pantalla */
  2004.   vpx1 = (getmaxx () + 1 - h0) / 2 + 1;
  2005.   vpy1 = getmaxy () + 1 <= h0 ? 0 : (getmaxy () + 1 - h0) / 2 + 1;
  2006.   vpx2 = vpx1 + h0 - 1;
  2007.   vpy2 = getmaxy () + 1 <= h0 ? getmaxy () : vpy1 + h0 - 1;
  2008.  
  2009.   setviewport (vpx1, vpy1, vpx2, vpy2, 0);
  2010. }
  2011.  
  2012. void a (int i)
  2013. {
  2014.   if (i > 0)
  2015.     {
  2016.       a (i-1); x += h; y -= h; plot;
  2017.       b (i-1); x += 2 * h; plot;
  2018.       d (i-1); x += h; y += h; plot;
  2019.       a (i-1);
  2020.     }
  2021. }
  2022.  
  2023. void b (int i)
  2024. {
  2025.   if (i > 0)
  2026.     {
  2027.       b (i-1); x -= h; y -= h; plot;
  2028.       c (i-1); y -= 2 * h; plot;
  2029.       a (i-1); x += h; y -= h; plot;
  2030.       b (i-1);
  2031.     }
  2032. }
  2033.  
  2034. void c (int i)
  2035. {
  2036.   if (i > 0)
  2037.     {
  2038.       c (i-1); x -= h; y += h; plot;
  2039.       d (i-1); x -= 2 * h; plot;
  2040.       b (i-1); x -= h; y -= h; plot;
  2041.       c (i-1);
  2042.     }
  2043. }
  2044.  
  2045. void d (int i)
  2046. {
  2047.   if (i > 0)
  2048.     {
  2049.       d (i-1); x += h; y += h; plot;
  2050.       a (i-1); y += 2 * h; plot;
  2051.       c (i-1); x -= h; y += h; plot;
  2052.       d (i-1);
  2053.     }
  2054. }
  2055. ende
  2056. begine " ESPIRAL "
  2057. /*
  2058.   Este programa dibuja espirales poligonales aleatorias.
  2059.  
  2060.   Funciones nuevas del C que aparecen en este ejemplo:
  2061.  
  2062.     - Librería <conio.h>
  2063.  
  2064.      · void gotoxy (int x, int y);
  2065.       Posiciona cursor en modo texto. Es el equivalente a moveto() en
  2066.       modo gráfico. La esquina superior izquierda en modo texto tiene
  2067.       coordenadas (1,1) mientras que en modo gráfico tiene coordeandas
  2068.       (0,0).
  2069.  
  2070.     - Librería <graphics.h>:
  2071.  
  2072.      · int getmaxcol (void);
  2073.       Devuelve el máximo color en el modo de gráfico corriente.
  2074.  
  2075.      · void cleardevice (void);
  2076.       Borra pantalla gráfica.
  2077.  
  2078.      · void setcolor (int color);
  2079.       Pone el color de dibujo corriente. El color 0 siempre corresponde al
  2080.       negro. Los colores disponibles dependen de la controladora gráfica. La
  2081.       función getmaxcol() nos informa del número de colores disponibles.
  2082.  
  2083.      · void circle (int x, int y, int radio);
  2084.       Dibuja un círculo en centro (x,y) y con radio dado.
  2085.  
  2086.     - Librería <time.h>:
  2087.  
  2088.      · time_t
  2089.       Tipo definido en el fichero time.h. Normalmente suele estar definida
  2090.       como long. Este tipo es usado por las funciones de tiempo.
  2091.  
  2092.      · time_t time (time_t *timer);
  2093.       Devuelve el tiempo corriente, en segundos, que ha transcurrido desde
  2094.       00:00:00 GMT, 1 de Enero de 1970, y almacena el valor en el lugar
  2095.       apuntado por timer. También devuelve dicho valor.
  2096.       El uso más común es:
  2097.         time_t t;
  2098.         t = time (NULL);
  2099.       Al pasar NULL estamos pasando un puntero que no apunta en ningún sitio
  2100.       y por lo tanto la función time se limita sólo a devolver el tiempo
  2101.       corriente.
  2102.  
  2103.      - Librería <math.h>:
  2104.  
  2105.       · double sin (double x);
  2106.        Devuelve el seno de x. El argumento x ha de estar en radianes.
  2107.  
  2108.       · double cos (double x);
  2109.        Devuelve el coseno de x. El argumento x ha de estar en radianes.
  2110.  
  2111.       · double pow (double x, double y);
  2112.        Devuelve x elevado a y.
  2113.  
  2114.       · double atan (double x);
  2115.        Devuelve el arco tangente de x.
  2116.  
  2117.  
  2118.   Observaciones que hay en el código fuente:
  2119.  
  2120.   1) Recordemos estas dos igualdades matemáticas:
  2121.  
  2122.       45° = π / 4
  2123.       tg (45°) = 1.0
  2124.  
  2125.      que son equivalentes a:
  2126.  
  2127.        π = 4 * 45°
  2128.        arctg (1.0) = 45°
  2129.  
  2130.      lo que nos da:
  2131.  
  2132.        π = 4 * arctg (1.0)
  2133.  
  2134.      De esta forma no tenemos que escribir el valor de pi.
  2135.  
  2136.   2) Las funciones trigonométricas de la librería <math.h> trabajan con
  2137.      radianes, y sin embargo, nuestro programa trabaja con grados. Por
  2138.      este motivo necesitamos una constante de conversión de grados sexa-
  2139.      gesimales a radianes. Recordar que 360° es equivalente a 2π radianes.
  2140.  
  2141.   3) Los bucles de este tipo:
  2142.  
  2143.       while (kbhit ())
  2144.         getch ();
  2145.  
  2146.      vacían el buffer de teclas. El objetivo de esto es impedir que se
  2147.      pueda escribir por adelantado.
  2148. */
  2149.  
  2150. #include <stdlib.h>   /* randomize (), random (), exit (), EXIT_FAILURE */
  2151. #include <time.h>     /* time (), time_t */
  2152. #include <math.h>     /* sin (), cos (), pow (), atan () */
  2153. #include <conio.h>    /* getch (), kbhit (), getche () */
  2154. #include <graphics.h> /* initgraph (), graphresult (), grOK, grapherrormsg (),
  2155.                          lineto (), moveto (), cleardevice (), getmaxx (),
  2156.                          getmaxy (), getmaxcol (), setcolor () */
  2157. #include <stdio.h>    /* printf (), NULL, scanf (), puts () */
  2158.  
  2159. /* macros: */
  2160.  
  2161. #define BOOLEAN int
  2162. #define TRUE    1
  2163. #define FALSE   0
  2164.  
  2165. #define ESC   27
  2166. #define ENTER 13
  2167.  
  2168. #define NUMESPIRALES 3
  2169.  
  2170. #define comparar(l1,l2,l3) ((l1) == (l2) || (l1) == (l3))
  2171. #define entre(x,x1,x2) ((x) >= (x1) && (x) <= (x2))
  2172.  
  2173. /* Variables globales: */
  2174.  
  2175. int maxx, maxy, maxcol; /* máxima x, máxima y y máximo color */
  2176. BOOLEAN espiral_centrada,
  2177.         escribir_con_tecla, escribir_circulos, escribir_valores;
  2178. int segundos_pausa; /* segundos de pausa entre espirales */
  2179. int borrar_pantalla_entre_espirales; /* 0: no, 1: sí, 2: aleatorio */
  2180. int tipo_espiral; /* toma los valores de 0 a 8 */
  2181. double grad; /* constante de conversión grados/radianes */
  2182. double pi; /* número pi */
  2183.  
  2184. /* prototipos de funciones: */
  2185.  
  2186. BOOLEAN preguntar (void);
  2187. void espiral (void);
  2188.  
  2189. /* definición de funciones: */
  2190.  
  2191. void prlec5_6 (void)
  2192. {
  2193.   int driver = DETECT, modo, codigo_de_error;
  2194.   time_t tiempo;
  2195.   BOOLEAN salir;
  2196.  
  2197.   initgraph (&driver, &modo, "");
  2198.   codigo_de_error = graphresult ();
  2199.   if (codigo_de_error != grOk)
  2200.     {
  2201.        printf ("Error gráfico: %s\n", grapherrormsg (codigo_de_error));
  2202.        printf ("Presiona una tecla para finalizar.");
  2203.        getch ();
  2204.        exit (EXIT_FAILURE);
  2205.     }
  2206.   maxx = getmaxx ();
  2207.   maxy = getmaxy ();
  2208.   maxcol = getmaxcolor ();
  2209.   closegraph ();
  2210.  
  2211.   pi = 4.0 * atan (1.0); /* ver observación 1 */
  2212.   grad = pi / 180.0; /* ver observación 2 */
  2213.  
  2214.   randomize (); /* inicializa el generador de números aleatorios */
  2215.  
  2216.   while (preguntar ())
  2217.     {
  2218.       initgraph (&driver, &modo, "");
  2219.  
  2220.       while (kbhit ()) /* ver observación 3 */
  2221.         getch ();
  2222.  
  2223.       salir = FALSE;
  2224.  
  2225.       while (! salir)
  2226.         {
  2227.           espiral (); /* dibuja espiral */
  2228.           if (segundos_pausa < 0)
  2229.             {
  2230.               if (getch () == ESC)
  2231.                 salir = TRUE;
  2232.             }
  2233.           else
  2234.             {
  2235.               tiempo = time (NULL) + segundos_pausa;
  2236.               while (! kbhit () && time (NULL) < tiempo)
  2237.                 ; /* espera la pulsación de una tecla o la consumición de tiempo */
  2238.               if (kbhit ())
  2239.                 if (getch () == ESC)
  2240.                   salir = TRUE;
  2241.             }
  2242.         }
  2243.  
  2244.       if (kbhit ()) /* vacía posibles teclas */
  2245.         getch ();
  2246.  
  2247.       closegraph (); /* vuelve al modo texto */
  2248.     }
  2249. }
  2250.  
  2251. /*
  2252.    Esta función inicializa valores necesarios para el dibujo de la espiral
  2253.    y devuelve TRUE para seguir o FALSE para salir del programa
  2254. */
  2255. BOOLEAN preguntar (void)
  2256. {
  2257.   char ch;
  2258.   BOOLEAN seguir;
  2259.  
  2260.   /* valores por defecto */
  2261.   espiral_centrada = TRUE;
  2262.   escribir_con_tecla = escribir_circulos = escribir_valores = FALSE;
  2263.   segundos_pausa = 2;
  2264.   borrar_pantalla_entre_espirales = 1;
  2265.   tipo_espiral = 1;
  2266.  
  2267.   puts ("PROGRAMA ESPIRAL ");
  2268.   printf ("\n¿Valores por defecto (S/N, ENTER: Sí, ESC: Salir)? ");
  2269.   do
  2270.     {
  2271.       ch = getch ();
  2272.     } while (ch != ENTER && ch != ESC && ! comparar (ch, 's', 'S') && ! comparar (ch, 'n', 'N'));
  2273.   if (ch == ESC)
  2274.     seguir = FALSE;
  2275.   else
  2276.     {
  2277.       seguir = TRUE;
  2278.       if (comparar (ch, 'n', 'N'))
  2279.         {
  2280.           printf ("\n¿Centro de la espiral en centro de la pantalla (S/N, ENTER: Sí)? ");
  2281.           do
  2282.             {
  2283.               ch = getch ();
  2284.             } while (ch != ENTER && ! comparar (ch, 's', 'S') && ! comparar (ch, 'n', 'N'));
  2285.           espiral_centrada = ! comparar (ch, 'n', 'N');
  2286.           printf ("\n¿Pausa de 2 segundos entre espiral y espiral (S/N, ENTER: Sí)? ");
  2287.           do
  2288.             {
  2289.               ch = getch ();
  2290.             } while (ch != ENTER && ! comparar (ch, 's', 'S') && ! comparar (ch, 'n', 'N'));
  2291.           if (comparar (ch, 'n', 'N'))
  2292.             {
  2293.               printf ("\nIntroduce segundos de pausa entre espirales (< 0 para esperar tecla): ");
  2294.               scanf ("%d", &segundos_pausa);
  2295.             }
  2296.           printf ("\n¿Escribir espiral mediante pulsación de tecla (S/N, ENTER: No)? ");
  2297.           do
  2298.             {
  2299.               ch = getch ();
  2300.             } while (ch != ENTER && ! comparar (ch, 's', 'S') && ! comparar (ch, 'n', 'N'));
  2301.           escribir_con_tecla = comparar (ch, 's', 'S');
  2302.           printf ("\n¿Escribir valores de algunas variables en dibujo (S/N, ENTER: No)? ");
  2303.           do
  2304.             {
  2305.               ch = getch ();
  2306.             } while (ch != ENTER && ! comparar (ch, 's', 'S') && ! comparar (ch, 'n', 'N'));
  2307.           escribir_valores = comparar (ch, 's', 'S');
  2308.           printf ("\n¿Escribir también círculos de radio aleatorio (S/N, ENTER: No)?: ");
  2309.           do
  2310.             {
  2311.               ch = getch ();
  2312.             } while (ch != ENTER && ! comparar (ch, 's', 'S') &&
  2313.                      ! comparar (ch, 'n', 'N') && ! comparar (ch, 'a', 'A'));
  2314.           escribir_circulos = comparar (ch, 's', 'S');
  2315.           printf ("\n¿Borrar pantalla entre espirales (S o ENTER: Sí, N: No, A: Aleatorio)?: ");
  2316.           do
  2317.             {
  2318.               ch = getch ();
  2319.             } while (ch != ENTER && ! comparar (ch, 's', 'S') &&
  2320.                      ! comparar (ch, 'n', 'N') && ! comparar (ch, 'a', 'A'));
  2321.           borrar_pantalla_entre_espirales = ch == ENTER || comparar (ch, 's', 'S') ? 1 :
  2322.                                             comparar (ch, 'n', 'N') ? 0 : 2;
  2323.           puts ("\nTipos de espirales (r: radio, a: ángulo):");
  2324.           puts (" 0: espiral aleatoria entre una de las 8 siguientes");
  2325.           puts (" 1: x = r * seno (a); y = r * coseno (a)");
  2326.           puts (" 2: x = r * coseno(a)*coseno(a)*coseno(a); y = r * seno(a)*seno(a)*seno(a)");
  2327.           puts (" 3: exponente1 y exponente2 aleatorios, 0 ≤ exponente1, exponente2 ≤ 3,");
  2328.           puts ("    exponente1 + exponente2 es un número impar;");
  2329.           puts ("    x = r * potencia(seno(a),exponente1) * potencia(coseno(a)*exponente2);");
  2330.           puts ("    x = r * potencia(seno(a),exponente2) * potencia(coseno(a)*exponente1);");
  2331.           puts ("    [las funciones 1 y 2 son un caso particular de esta funión]");
  2332.           printf ("\nIntroduce número correspondiente al tipo de espiral (0-8, ENTER: 0): ");
  2333.           do
  2334.             {
  2335.               ch = getch ();
  2336.             } while (ch != ENTER && ! entre (ch, '0', '0' + NUMESPIRALES));
  2337.           tipo_espiral = ch == ENTER ? 0 : ch - '0';
  2338.         }
  2339.     }
  2340.  
  2341.   return (seguir);
  2342. }
  2343.  
  2344. /*
  2345.   Dibuja espiral.
  2346. */
  2347. void espiral (void)
  2348. {
  2349.   double radio, angulo;
  2350.   double incr_radio, incr_angulo;
  2351.   double seno, coseno;
  2352.   int x, y;
  2353.   int tipo_espiral_local;
  2354.   int centrox, centroy; /* coordenadas del centro de la pantalla */
  2355.   int exponente1, exponente2;
  2356.   int radio_circulo;
  2357.  
  2358.   incr_angulo = random (3600) / 10.0; /* 0.0 ≤ incr_angulo ≤ 359.9 */
  2359.   incr_radio = 1.0 + random (300) / 100.0; /* 1.0 ≤ incr_radio ≤ 3.99 */
  2360.  
  2361.   radio = angulo = 0.0;
  2362.  
  2363.   if (escribir_circulos)
  2364.     radio_circulo = 1 + random (maxy / 2); /* 1 ≤ radio_circulo ≤ maxy / 2*/
  2365.  
  2366.   if (espiral_centrada)
  2367.     {
  2368.       centrox = (maxx + 1) / 2;
  2369.       centroy = (maxy + 1) / 2;
  2370.       x = centrox;
  2371.       y = centroy;
  2372.     }
  2373.   else
  2374.     {
  2375.       x = maxx;
  2376.       y = maxy;
  2377.     }
  2378.  
  2379.   setcolor (1 + random (maxcol)); /* genera color disponible excepto el 0 (negro) */
  2380.  
  2381.   if (borrar_pantalla_entre_espirales == 1 ||
  2382.       borrar_pantalla_entre_espirales == 2 && random (2))
  2383.     cleardevice ();
  2384.  
  2385.   tipo_espiral_local = tipo_espiral == 0 ? random (NUMESPIRALES) + 1 : tipo_espiral;
  2386.   if (tipo_espiral_local == 3)
  2387.     {
  2388.       exponente1 = random (4);
  2389.       exponente2 = random (4);
  2390.       if ((exponente1 + exponente2) % 2 == 0)
  2391.         (random (2) ? exponente1 : exponente2)++;
  2392.     }
  2393.  
  2394.   moveto (x, y);
  2395.  
  2396.   while ((espiral_centrada && (x >= 0 && x <= getmaxx () || y >= 0 && y <= getmaxy ())) ||
  2397.          (! espiral_centrada && (x > 0 || y > 0)))
  2398.     {
  2399.       radio += incr_radio;
  2400.       angulo += incr_angulo;
  2401.  
  2402.       seno = sin (angulo * grad);
  2403.       coseno = cos (angulo * grad);
  2404.  
  2405.       switch (tipo_espiral_local)
  2406.         {
  2407.           case 1:
  2408.             x = (espiral_centrada ? centrox : x) + radio * seno;
  2409.             y = (espiral_centrada ? centroy : y) + radio * coseno;
  2410.             break;
  2411.           case 2:
  2412.             x = (espiral_centrada ? centrox : x) + radio * (coseno * coseno * coseno);
  2413.             y = (espiral_centrada ? centroy : y) + radio * (seno * seno * seno);
  2414.             break;
  2415.           case 3:
  2416.             x = (espiral_centrada ? centrox : x) + radio * pow (seno, exponente1) * pow (coseno, exponente2);
  2417.             y = (espiral_centrada ? centroy : y) + radio * pow (seno, exponente2) * pow (coseno, exponente1);
  2418.             break;
  2419.         }
  2420.       if (escribir_con_tecla)
  2421.         getch ();
  2422.       lineto (x, y);
  2423.       if (escribir_circulos)
  2424.         circle (x, y, radio_circulo);
  2425.     }
  2426.  
  2427.   if (escribir_valores)
  2428.     {
  2429.       gotoxy (1, 1);
  2430.       printf ("Incremento del ángulo: %lg\n", incr_angulo);
  2431.       printf ("Incremento del radio: %lg\n", incr_radio);
  2432.       if (escribir_circulos)
  2433.         printf ("Radio del círculo: %d\n", radio_circulo);
  2434.     }
  2435. }
  2436. ende
  2437. begine " POLIGONO "
  2438. /*
  2439.   Dibuja un polígono de una forma recursiva.
  2440.  
  2441.   Al polígono que dibuja este programa también se le llama fractal ya que
  2442.   está definido en términos recursivos y su aspecto es siempre el mismo
  2443.   independientemente de su tamaño.
  2444.  
  2445.   Todas las funciones utilizadas ya han sido explicadas en ejemplos anteriores
  2446.   y en teoría. En cuanto a la lógica del programa es muy simple y se entiende
  2447.   enseguida.
  2448. */
  2449.  
  2450. /* Ficheros a incluir: */
  2451.  
  2452. #include <stdio.h>    /* printf (), puts (), scanf () */
  2453. #include <math.h>     /* atan () */
  2454. #include <conio.h>    /* getch (), kbhit () */
  2455. #include <stdlib.h>   /* exit (), EXIT_FAILURE, EXIT_SUCCESS */
  2456. #include <graphics.h> /* initgraph (), graphresult (), grOK, grapherrormsg (),
  2457.                          closegraph (), DETECT, lineto (), moveto () */
  2458.  
  2459. /* macros: */
  2460.  
  2461. #define ESC     27
  2462. #define BOOLEAN int
  2463. #define TRUE    1
  2464. #define FALSE   0
  2465.  
  2466. /* variables globales: */
  2467.  
  2468. double rad_a_grad;
  2469. double posx, posy;
  2470.  
  2471. /* prototipos de las funciones utilizadas: */
  2472.  
  2473. BOOLEAN leer_datos (int *numero_de_lados, int *orden);
  2474. void dibuja_poligono (int numero_de_lados, int orden);
  2475. void dibuja_lado (double la, double ang, int n);
  2476. void dibuja_linea (double lon, double a);
  2477.  
  2478. /* definición de las funciones: */
  2479.  
  2480. /*
  2481.   Función principal
  2482. */
  2483.  
  2484. void main (void)
  2485. {
  2486.   int numero_de_lados_poligono, orden_poligono;
  2487.   int driver = DETECT, modo, codigo_de_error;
  2488.   char ch;
  2489.  
  2490.   initgraph (&driver, &modo, "");
  2491.   codigo_de_error = graphresult();
  2492.   if (codigo_de_error != grOk)
  2493.     {
  2494.        printf ("Error gráfico: %s\n", grapherrormsg (codigo_de_error));
  2495.        printf ("Presiona un tecla para finalizar.");
  2496.        getch ();
  2497.        exit (EXIT_FAILURE);
  2498.     }
  2499.  
  2500.   /* ver el significado de esta variable en la variable grad del ejemplo
  2501.      anterior: espiral */
  2502.   rad_a_grad = atan (1.0) / 45.0;
  2503.  
  2504.   closegraph ();
  2505.   while (leer_datos (&numero_de_lados_poligono, &orden_poligono))
  2506.     {
  2507.       initgraph (&driver, &modo, "");
  2508.       dibuja_poligono (numero_de_lados_poligono, orden_poligono);
  2509.       ch = getch ();
  2510.       closegraph ();
  2511.       if (ch == ESC) /* salir con exit() sin hacer closegraph() es desastroso */
  2512.         exit (EXIT_SUCCESS);
  2513.     }
  2514.  
  2515.   closegraph ();
  2516. }
  2517.  
  2518. /*
  2519.   Lee las características del polígono y permite abandonar el programa.
  2520. */
  2521.  
  2522. BOOLEAN leer_datos (int *numero_de_lados, int *orden)
  2523. {
  2524.   BOOLEAN seguir;
  2525.  
  2526.   puts ("POLIGONO RECURSIVO");
  2527.   puts ("\nPara salir del programa durante la traza del dibujo, pulsar ESC.");
  2528.   do
  2529.     {
  2530.       printf ("\nIntroduce número de lados del polígono (≥ 1, 0 para salir): ");
  2531.       scanf ("%d", numero_de_lados); /* no sería correcto &numero_de_lados porque
  2532.                                         numero_de_lados ya es un puntero */
  2533.     } while (*numero_de_lados < 0);
  2534.   if (*numero_de_lados == 0)
  2535.     seguir = FALSE;
  2536.   else
  2537.     {
  2538.       seguir = TRUE;
  2539.       do
  2540.         {
  2541.           printf ("\nIntroduce orden del polígono (≥ 1): ");
  2542.           scanf ("%d", orden);
  2543.         } while (*orden < 1);
  2544.     }
  2545.   return (seguir);
  2546. }
  2547.  
  2548. /*
  2549.   Dibuja un polígono de un cierto número de lados y de un cierto orden
  2550. */
  2551.  
  2552. void dibuja_poligono (int numero_de_lados, int orden)
  2553. {
  2554.     register int i;
  2555.     double lado, angulo;
  2556.  
  2557.     /*
  2558.        el siguiente if es para que la figura salga más o menos centrada en
  2559.        la pantalla
  2560.     */
  2561.     if (numero_de_lados == 1 || numero_de_lados == 2)
  2562.       {
  2563.         lado = getmaxy () / 2;
  2564.         posx = getmaxx () / 2.0 + (lado / 2) * (numero_de_lados == 1 ? -1 : 1);
  2565.         posy = getmaxy () / 2.0;
  2566.       }
  2567.     else
  2568.       {
  2569.         lado = getmaxy () / numero_de_lados;
  2570.         posx = getmaxx () / 2.0 + lado / 2;
  2571.         posy = getmaxy () / 2.0 - lado;
  2572.       }
  2573.     moveto ((int) posx, (int) posy);
  2574.     angulo = 360.0 / numero_de_lados;
  2575.     for (i = 1; i <= numero_de_lados; i++)
  2576.       dibuja_lado (lado, -angulo * i, orden);
  2577. }
  2578.  
  2579. /*
  2580.   Dibuja un lado recursivo hacia el ángulo angulo y de orden ord
  2581. */
  2582.  
  2583. void dibuja_lado (double lado, double angulo, int ord)
  2584. {
  2585.   if (ord <= 1)
  2586.     dibuja_linea (lado, angulo);
  2587.   else
  2588.     {
  2589.       lado /= 3.0;
  2590.       ord--;
  2591.       /* cada lado consta de 4 líneas; se puede modifica el número de líneas
  2592.          de cada lado para hacer otras figuras */
  2593.       dibuja_lado (lado, angulo, ord);
  2594.       dibuja_lado (lado, angulo + 60, ord);
  2595.       dibuja_lado (lado, angulo - 60, ord);
  2596.       dibuja_lado (lado, angulo, ord);
  2597.     }
  2598. }
  2599.  
  2600. /*
  2601.   Dibuja una línea de longitud lon desde la posición actual hacia el
  2602.   ángulo a
  2603. */
  2604.  
  2605. void dibuja_linea (double lon, double a)
  2606. {
  2607.   posx += (lon * cos (a * rad_a_grad));
  2608.   posy -= (lon * sin (a * rad_a_grad));
  2609.  
  2610.   if (kbhit () && getch () == ESC) /* la comparación getch()==ESC sólo se ... */
  2611.     exit (EXIT_SUCCESS); /* ... hace si se cumple la primera condición: kbhit() */
  2612.   lineto ((int) posx, (int) posy);
  2613. }
  2614. ende
  2615. endt
  2616. end lección 6
  2617.  
  2618. ; LECCION 6
  2619. begin
  2620. begine " TRES EN RAYA "
  2621. /*
  2622.   Programa que permite jugar al usuario a las tres en raya con la computadora.
  2623.  
  2624.   El programa representa la matriz de tres en raya como un array de caracteres
  2625.   de 3 por 3. El jugador siempre es X y la computadora O.
  2626.  
  2627.   Cuando mueve el jugador, el carácter X se coloca en la posición especificada
  2628.   de la matriz. Cuando le toca mover a la computadora, recorre la matriz y pone
  2629.   O en la primera posición vacía de la matriz. Si no encuentra una posición
  2630.   vacía, lo indica y sale.
  2631. */
  2632.  
  2633. #include <stdio.h>  /* printf (), scanf () */
  2634. #include <stdlib.h> /* exit ()             */
  2635. #include <conio.h>  /* getch ()            */
  2636.  
  2637. #define ESPACIO ' '
  2638.  
  2639. char matriz[3][3] =
  2640.   {
  2641.     ESPACIO, ESPACIO, ESPACIO,
  2642.     ESPACIO, ESPACIO, ESPACIO,
  2643.     ESPACIO, ESPACIO, ESPACIO
  2644.   };
  2645.  
  2646. void obtener_movimiento_de_jugador (void);
  2647. void obtener_movimiento_de_computadora (void);
  2648. void mostrar_matriz (void);
  2649. char comprobar (void);
  2650.  
  2651. void main (void)
  2652. {
  2653.   char hecho = ESPACIO;
  2654.  
  2655.   printf ("JUEGO DE LAS TRES EN RAYA.\n\n");
  2656.  
  2657.   do
  2658.     {
  2659.       mostrar_matriz ();
  2660.       obtener_movimiento_de_jugador ();
  2661.       hecho = comprobar (); /* ver si gana el jugador */
  2662.       if (hecho != ESPACIO)
  2663.         break;
  2664.       obtener_movimiento_de_computadora ();
  2665.       hecho = comprobar (); /* ver si gana la computadora */
  2666.     } while (hecho == ESPACIO);
  2667.  
  2668.   if (hecho == 'X')
  2669.     printf ("\n¡HAS GANADO!\n");
  2670.   else
  2671.     printf ("\n¡YO GANO!\n");
  2672.  
  2673.   mostrar_matriz (); /* mostrar las posiciones finales */
  2674.   printf ("\n\nPulsa cualquier tecla para finalizar. ");
  2675.   getch ();
  2676. }
  2677.  
  2678. void obtener_movimiento_de_jugador (void)
  2679. {
  2680.   int x, y;
  2681.  
  2682.   printf ("\nIntroduzca sus coordenadas de la X (1≤fila≤3, 1≤columna≤3): ");
  2683.   scanf ("%d%d", &x, &y);
  2684.   x--;
  2685.   y--;
  2686.   if (matriz[x][y] != ESPACIO)
  2687.     {
  2688.       printf ("Movimiento inválido, prueba de nuevo.\n");
  2689.       obtener_movimiento_de_jugador ();
  2690.     }
  2691.   else
  2692.     matriz[x][y] = 'X';
  2693. }
  2694.  
  2695. void obtener_movimiento_de_computadora (void)
  2696. {
  2697.   int encontrado = 0;
  2698.   register int i, j;
  2699.  
  2700.   for (i = 0; ! encontrado && i < 3; i++)
  2701.     for (j = 0; ! encontrado && j < 3; j++)
  2702.       if (matriz[i][j] == ESPACIO)
  2703.         encontrado = 1;
  2704.  
  2705.   if (encontrado)
  2706.     matriz[i-1][j-1] = 'O';
  2707.   else
  2708.     {
  2709.       printf ("Tablero completo.\n");
  2710.       exit (0);
  2711.     }
  2712. }
  2713.  
  2714. void mostrar_matriz (void)
  2715. {
  2716.   register int i;
  2717.  
  2718.   printf ("\n    1   2   3");
  2719.   printf ("\n  ┌───┬───┬───┐");
  2720.   for (i = 0; i < 3; i++)
  2721.     {
  2722.       printf ("\n%d │ %c │ %c │ %c │", i+1, matriz[i][0], matriz[i][1], matriz[i][2]);
  2723.       if (i != 2)
  2724.         printf ("\n  ├───┼───┼───┤");
  2725.     }
  2726.   printf ("\n  └───┴───┴───┘");
  2727. }
  2728.  
  2729. char comprobar (void)
  2730. {
  2731.   register int t;
  2732.  
  2733.   /* comprobar filas */
  2734.   for (t = 0; t < 3; t++)
  2735.     if (matriz[t][0] == matriz[t][1] && matriz[t][1] == matriz[t][2])
  2736.       return matriz[t][0];
  2737.  
  2738.   /* comprobar columnas */
  2739.   for (t = 0; t < 3; t++)
  2740.     if (matriz[0][t] == matriz[1][t] && matriz[1][t] == matriz[2][t])
  2741.       return matriz[0][t];
  2742.  
  2743.   /* comprobar diagonal principal */
  2744.   if (matriz[0][0] == matriz[1][1] && matriz[1][1] == matriz[2][2])
  2745.     return matriz[0][0];
  2746.  
  2747.   /* comprobar diagonal inversa */
  2748.   if (matriz[0][2] == matriz[1][1] && matriz[1][1] == matriz[2][0])
  2749.     return matriz[0][2];
  2750.  
  2751.   return ESPACIO;
  2752. }
  2753. ende
  2754. begine " CUADRADO MAGICO "
  2755. /*
  2756.   Este programa genera e imprime un cuadrado mágico de dimensión N (siendo N
  2757.   un número entero, positivo e impar).
  2758.  
  2759.   Un cuadrado mágico de dimensión N es una matriz cuadrada de orden N,
  2760.   conteniendo los números naturales de 1 a N*N, tal que coinciden la suma
  2761.   de los números de una cualquiera de las filas o columnas, o de una de
  2762.   las diagonales principales.
  2763.  
  2764.   El cuadrado se construye mediante las siguientes reglas:
  2765.  
  2766.     - El número 1 se coloca en la casilla central de la primera fila.
  2767.     - Cada número siguiente se coloca en la casilla correspondiente a la
  2768.       fila anterior y columna posterior.
  2769.     - Si el número sigue a un múltiplo de N, no se aplica la regla anterior,
  2770.       sino que se coloca en la casilla de la fila posterior e igual columna.
  2771.     - Se considera que la fila anterior a la primera es la última, y la
  2772.       columna posterior a la última es la primera.
  2773.  
  2774.   Una observación acerca de la implementación de este programa: En la declara-
  2775.   ción de la matriz se ha puesto MAXIMA_DIMENSION+1 porque los elementos de
  2776.   la matriz correspondientes a los índices 0 se han despreciado; con esto se
  2777.   ha perseguido hacer la lógica del programa más simple.
  2778. */
  2779.  
  2780. #include <stdio.h> /* printf (), scanf () */
  2781. #include <conio.h> /* getch ()  */
  2782.  
  2783. #define MAXIMA_DIMENSION 19
  2784.  
  2785. void main (void)
  2786. {
  2787.   int matriz[MAXIMA_DIMENSION+1][MAXIMA_DIMENSION+1];
  2788.   int dimension, numero, fila, columna;
  2789.  
  2790.   printf ("CUADRADO MAGICO\n\n");
  2791.   do
  2792.     {
  2793.       printf ("Introduce dimensión (impar y entre 1-%d): ", MAXIMA_DIMENSION);
  2794.       scanf ("%d", &dimension);
  2795.     } while (dimension % 2 == 0 || dimension < 1 || dimension > MAXIMA_DIMENSION);
  2796.  
  2797.   fila = 1;
  2798.   columna = dimension / 2 + 1;
  2799.   for (numero = 1; numero <= dimension * dimension; numero++)
  2800.     {
  2801.       matriz[fila][columna] = numero;
  2802.       if (numero % dimension == 0)
  2803.         fila++;
  2804.       else
  2805.         {
  2806.           fila = fila == 1 ? dimension : fila - 1;
  2807.           columna = columna == dimension ? 1 : columna + 1;
  2808.         }
  2809.     }
  2810.  
  2811.   printf ("\nEl cuadrado mágico de dimensión %d es:\n\n", dimension);
  2812.   for (fila = 1; fila <= dimension; fila++)
  2813.     {
  2814.       for (columna = 1; columna <= dimension; columna++)
  2815.         printf ("%4d", matriz[fila][columna]);
  2816.       printf ("\n");
  2817.     }
  2818.  
  2819.   printf ("\nPulsa una tecla para finalizar.");
  2820.   getch ();
  2821. }
  2822. ende
  2823. begine " MEJOR DESGLOSE DE MONEDA "
  2824. /*
  2825.   Este programa recibe como dato un número entero positivo, correspondiente
  2826.   a una cantidad de dinero, y calcula e imprime el mejor desglose de moneda
  2827.   (mínimo número de unidades monetarias).
  2828.  
  2829.   Las unidades monetarias existentes son:
  2830.  
  2831.     10000, 5000, 2000, 1000, 500, 200, 100, 50, 25, 10, 5, 2, 1.
  2832.  
  2833.   El programa almacenará estas cantidades ordenadas en un vector y desglosará
  2834.   la cantidad en orden creciente de las componentes del vector.
  2835. */
  2836.  
  2837. void main (void)
  2838. {
  2839.   int unidades [] =
  2840.     { 10000, 5000, 2000, 1000, 500, 200, 100, 50, 25, 10, 5, 2, 1 };
  2841.   int i;
  2842.   long cantidad, num;
  2843.  
  2844.   printf ("DESGLOSE DE MONEDA\n\n");
  2845.   printf ("Las unidades monetarias son:\n");
  2846.   printf ("10000, 5000, 2000, 1000, 500, 200, 100, 50, 25, 10, 5, 2, 1.\n\n");
  2847.   printf ("Escribe la cantidad a desglosar (<= 0 para terminar): ");
  2848.   scanf ("%ld", &cantidad);
  2849.   while (cantidad > 0)
  2850.     {
  2851.       i = -1;
  2852.       while (cantidad > 0)
  2853.         {
  2854.           if ((num = cantidad / unidades[++i]) > 0)
  2855.             printf ("  %ld %s%s de %d\n", num,
  2856.                     unidades[i] <= 500 ? "moneda" : "billete",
  2857.                     num == 1 ? "" : "s", unidades[i]);
  2858.           cantidad -= num * unidades[i];
  2859.         }
  2860.       printf ("\nEscribe la cantidad a desglosar (<= 0 para terminar): ");
  2861.       scanf ("%ld", &cantidad);
  2862.     }
  2863. }
  2864. ende
  2865. begine " METODO DE ELIMINACION DE GAUSS "
  2866. /*
  2867.   El objetivo de este programa es ejecutar el método de eliminación de Gauss
  2868.   sobre un sistema de ecuaciones introducido por el usuario.
  2869.  
  2870.   METODO DE ELIMINACION DE GAUSS PARA LA RESOLUCION DE SISTEMAS DE ECUACIONES:
  2871.  
  2872.   En muchísimos cálculos científicos y de ingeniería, las soluciones a un
  2873.   problema se presentan en forma de sistema de ecuaciones con varias incóg-
  2874.   nitas. Un ejemplo de sistema sería:
  2875.  
  2876.       x1 +  2 x2 - x3 =  2
  2877.     5 x1 + 12 x2 + x3 = 32
  2878.     3 x1 +  9 x2 + x3 = 24
  2879.  
  2880.   En este caso se trata de un sistema de tres ecuaciones con tres incógnitas.
  2881.   Los números por los que se multiplican las incógnitas se llaman coeficientes,
  2882.   y constantes los que ocupan los miembros derechos de las ecuaciones. En
  2883.   general, puede haber sistemas con cualquier número de ecuaciones y el mismo
  2884.   número de incógnitas. Algunos de estos sistemas no tienen solución, y otros
  2885.   tienen muchas. Aquí nos limitaremos a los sistemas "bien educados", con una
  2886.   solución única que es el conjunto de valores de las incógnitas que satisface
  2887.   todas las ecuaciones. Por razones de simplicidad, en los ejemplos haremos
  2888.   referencia únicamente a sistemas de tres ecuaciones con tres incógnitas.
  2889.  
  2890.   Hay varias técnicas de resolución de sistemas de ecuaciones. La de eliminación
  2891.   de Gauss es muy sencilla, pero a la vez muy potente. No se trata de un número
  2892.   de aproximación, y funciona con sistemas de cualquier número de ecuaciones y
  2893.   el mismo número de incógnitas. Admite pruebas para interrumpir el proceso si
  2894.   el sistema carece de solución. En el método de eliminación de Gauss, los coe-
  2895.   ficientes y las constantes se consideran como elementos de una matriz; el
  2896.   proceso tiene dos fases:
  2897.  
  2898.   En primer lugar se restan sistemáticamente múltiplos de unas ecuaciones (o
  2899.   filas de matries) a otras hasta crear una matriz triangular en la que todos
  2900.   los elementos situados por debajo de la diagonal principal sean nulos.
  2901.  
  2902.   En segundo lugar se procede a la restitución, partiendo de la fila inferior
  2903.   y avanzando hacia arriba, hasta obtener los valores de todas las incógnitas.
  2904.  
  2905.   Apliquemos este método al ejemplo anterior:
  2906.  
  2907.       x1 +  2 x2 - x3 =  2   (1)
  2908.     5 x1 + 12 x2 + x3 = 32   (2)
  2909.     3 x1 +  9 x2 + x3 = 24   (3)
  2910.  
  2911.   El primer paso es eliminar los coeficientes segundo y tercero de la primera
  2912.   columna. Para ello se substrae a la segunda ecuación la primera multiplicada
  2913.   por 5, y a la tercera también la primera multiplicada por 3:
  2914.  
  2915.                      x1 + 2 x2 -   x3 =  2   (1)
  2916.     (2) -5 * (1)          2 x2 + 6 x3 = 22   (4)
  2917.     (3) -3 * (1)          3 x2 + 4 x3 = 18   (5)
  2918.  
  2919.   A continuación se elimina el último coeficiente de la segunda columna restando
  2920.   a la tercera ecuación la segunda multiplicada por 3/2:
  2921.  
  2922.                      x1 + 2 x2 -   x3 =   2   (1)
  2923.                           2 x2 + 6 x3 =  22   (4)
  2924.     (5) - 3/2 * (4)            - 5 x3 = -15   (6)
  2925.  
  2926.   Ahora los coeficientes de las ecuaciones forman ya una matriz triangular. El
  2927.   proceso de restitución empieza, pues, por la tercera ecuación:
  2928.  
  2929.      x3 = (-15) / (-5) = 3
  2930.      x2 = (22 - 6 x3) / 2 = (22 - 6 * 3) / 2 = 2
  2931.      x1 = (2 - 2 x2 + x3) / 1 = (2 - 2 * 2 + 3) / 1 = 1
  2932.  
  2933.   La solución es, pues;
  2934.  
  2935.     x1 = 1, x2 = 2, x3 = 3.
  2936.  
  2937.   PROGRAMACION DEL METODO DE GAUSS:
  2938.  
  2939.   En el programa, los coeficientes y las constantes pasan a convertirse en
  2940.   elementos de una matriz, identificados mediante dos subíndices:
  2941.  
  2942.     a11 x1 + a12 x2 + a13 x3 = a14
  2943.     a21 x1 + a22 x2 + a23 x3 = a24
  2944.     a31 x1 + a32 x2 + a33 x3 = a34
  2945.  
  2946.   Como todas las operaciones se ejecutarán sobre los elementos de la matriz,
  2947.   pueden omitirse las incógnitas:
  2948.  
  2949.     a11  a12  a13  a14
  2950.     a21  a22  a23  a24
  2951.     a31  a32  a33  a34
  2952.  
  2953.   Sean aij el elemento general de la matriz, situado en la fila i y la
  2954.   columna j. En general, la matriz tendrá n filas y n+1 columnas.
  2955.  
  2956.   El algoritmo de creación de la matriz triangular sobre la diagonal prin-
  2957.   cipal es el siguiente:
  2958.  
  2959.     Para k = 1 hasta n - 1, repetir el proceso:
  2960.       Para i = k + 1 hasta n, repetir el proceso:
  2961.         Hacer el multiplicador m = aik / akk.
  2962.         Para j = k hasta n + 1, repetir el proceso:
  2963.           Hacer aij = aij - m * akj.
  2964.  
  2965.   El proceso de restitución queda descrito por este otro algoritmo:
  2966.  
  2967.     Para k = n hasta 1 en pasos de -1, repetir el proceso:
  2968.       Hacer el substraendo s = 0.
  2969.       Para j = k + 1 hasta n, repetir el proceso:
  2970.         Hacer s = s + akj * xj.
  2971.       Hacer xk = (akn+1 - s) / akk.
  2972.  
  2973.   Observa que la repetición del bucle interno ocurre cero veces si k = n.
  2974.  
  2975.   OBSERVACION SOBRE ESTA IMPLEMENTACION C:
  2976.  
  2977.   En C, los índices de los arrays empiezan por 0; sin embargo, la lógica
  2978.   está pensada a partir de 1.
  2979.  
  2980.   La solución para esto es:
  2981.  
  2982.     int x[N];
  2983.  
  2984.     #define X(i) (x[i-1])
  2985.  
  2986.   De esta forma, cuando hacemos:
  2987.  
  2988.     X(1);
  2989.  
  2990.   estamos haciendo, en realidad, esto:
  2991.  
  2992.     x[0];
  2993. */
  2994.  
  2995. #include <stdio.h>  /* printf (), scanf (), puts (), getch () */
  2996. #include <stdlib.h> /* exit () */
  2997.  
  2998. void main (void)
  2999. {
  3000.   /* MACROS */
  3001.   #define NUMERO_MAXIMO_DE_INCOGNITAS 10
  3002.   #define M(fila,columna) (matriz[fila-1][columna-1])
  3003.   #define X(incognita) (incognitas[incognita-1])
  3004.  
  3005.   /* VARIABLES LOCALES */
  3006.   float matriz[NUMERO_MAXIMO_DE_INCOGNITAS][NUMERO_MAXIMO_DE_INCOGNITAS+1],
  3007.         incognitas[NUMERO_MAXIMO_DE_INCOGNITAS];
  3008.   register int i, j, k;
  3009.   int n;
  3010.   float m, s; /* multiplicando y substraendo */
  3011.  
  3012.   /* IMPRESION DEL TITULO DEL PROGRAMA */
  3013.   puts ("RESOLUCION DE UN SISTEMA DE ECUACIONES POR EL METODO DE ELIMINACION DE GAUSS\n");
  3014.  
  3015.   /* RELLENO DE LA MATRIZ */
  3016.   do
  3017.     {
  3018.       printf ("Introduce número de incógnitas (1-%d): ",
  3019.               NUMERO_MAXIMO_DE_INCOGNITAS);
  3020.       scanf ("%d", &n);
  3021.     } while (n < 1 || n > NUMERO_MAXIMO_DE_INCOGNITAS);
  3022.   for (i = 1; i <= n; i++)
  3023.     {
  3024.       printf ("Introduce los %d coeficientes de la fila %d: ", n + 1, i);
  3025.       for (j = 1; j <= n + 1; j++)
  3026.         {
  3027.           float faux;
  3028.           scanf ("%g", &faux);
  3029.           M (i, j) = faux;
  3030.         }
  3031.     }
  3032.  
  3033.   /* IMPRESION DEL SISTEMA ECUACIONES INTRODUCIDO */
  3034.   puts ("\nEL SISTEMA DE ECUACIONES INTRODUCIDO ES:");
  3035.   for (i = 1; i <= n; i++)
  3036.     {
  3037.       j = 1;
  3038.       printf ("  %g x%d ", M (i, j), j);
  3039.       for (j++; j <= n; j++)
  3040.         printf ("%c %g x%d ", M (i, j) < 0 ? '-' : '+',
  3041.                 M (i, j) < 0 ? - M (i, j) : M (i, j), j);
  3042.       printf ("= %g\n", M (i, j));
  3043.     }
  3044.  
  3045.   /* CREACION DE UNA MATRIZ TRIANGULAR SOBRE LA DIAGONAL PRINCIPAL */
  3046.   for (k = 1; k <= n - 1; k++)
  3047.     for (i = k + 1; i <= n; i++)
  3048.       {
  3049.         if (M (k, k) == 0)
  3050.           {
  3051.             puts ("\nNo se puede resolver el problema por este método.");
  3052.             getch ();
  3053.             exit (0);
  3054.           }
  3055.         m = M (i, k) / M (k, k);
  3056.         for (j = k; j <= n + 1; j++)
  3057.           M (i, j) = M (i, j) - m * M (k ,j);
  3058.       }
  3059.  
  3060.   /* RESTITUCION */
  3061.   for (k = n; k >= 1; k--)
  3062.     {
  3063.       s = 0;
  3064.       if (k + 1 <= n)
  3065.         for (j = k + 1; j <= n; j++)
  3066.           s += M (k, j) * X (j);
  3067.       if (M (k ,k) == 0)
  3068.         {
  3069.           puts ("\nNo se puede resolver el problema por este método.");
  3070.           getch ();
  3071.           exit (0);
  3072.         }
  3073.       X (k) = (M (k, n + 1) - s) / M (k, k);
  3074.     }
  3075.  
  3076.   /* IMPRESION DE LA SOLUCION DEL SISTEMA DE ECUACIONES */
  3077.   puts ("\nLA SOLUCION DEL SISTEMA ES:");
  3078.   for (i = 1; i <= n; i++)
  3079.     printf ("  x%d = %g\n", i, X (i));
  3080.  
  3081.   puts ("\nPulsa una tecla para finalizar.");
  3082.   getch ();
  3083.  
  3084.   #undef X
  3085.   #undef M
  3086.   #undef DIMENSION_MAXIMA
  3087. }
  3088. ende
  3089. begine " JUEGO DEL RATON ATRAPADO "
  3090. /*
  3091.   Tenemos una matriz de 20x20. A los elementos del contorno los vamos a
  3092.   llamar -1 y tienen el significado de agua. En cada extremo va a haber
  3093.   un elemento con valor 1 que tiene el significado de salida o puente. El
  3094.   resto de las casillas van a tener el valor de 0. El programa pondrá un
  3095.   ratón en una posición aleatoria de la matriz y este ratón se irá moviendo
  3096.   aleatoriamente por la matriz hasta caer al agua, hasta salir por un
  3097.   puente o hasta dar 100 movimientos (en este caso el ratón se muere de
  3098.   hambre). Además también escribiremos una matriz de 20x20 que nos va a
  3099.   informar el número de veces que ha pasado el ratón por los elementos de
  3100.   la primera matriz.
  3101. */
  3102.  
  3103. #include <stdio.h>  /* printf () */
  3104. #include <conio.h>  /* getch ()  */
  3105. #include <stdlib.h> /* random () */
  3106.  
  3107. #define DIMENSION 20
  3108.  
  3109. #define BOOLEAN int
  3110. #define TRUE    1
  3111. #define FALSE   0
  3112.  
  3113. void inicializar (int m[][DIMENSION], int mapa[][DIMENSION], int *px, int *py,
  3114.                   BOOLEAN *pahogado, BOOLEAN *pescapado,
  3115.                   int *pnumero_de_movimientos);
  3116. void mover (int movimiento, int *px, int *py);
  3117. void comprobar (int m[][DIMENSION], int mapa[][DIMENSION], int x, int y,
  3118.                 BOOLEAN *pahogado, BOOLEAN *pescapado,
  3119.                 int *pnumero_de_movimientos);
  3120. void finalizar (int m[][DIMENSION], int mapa[][DIMENSION], int x, int y,
  3121.                 BOOLEAN ahog, BOOLEAN escap);
  3122.  
  3123. void main (void)
  3124. {
  3125.   int x, y, numero_de_movimientos, matriz[DIMENSION][DIMENSION],
  3126.       mapa_matriz[DIMENSION][DIMENSION];
  3127.   BOOLEAN ahogado, escapado;
  3128.  
  3129.   inicializar (matriz, mapa_matriz, &x, &y, &ahogado, &escapado,
  3130.                &numero_de_movimientos);
  3131.   while (numero_de_movimientos < 100 && ! ahogado && ! escapado)
  3132.     {
  3133.       mover (random (4) + 1, &x, &y);
  3134.       comprobar (matriz, mapa_matriz, x, y,
  3135.                  &ahogado, &escapado, &numero_de_movimientos);
  3136.     }
  3137.   finalizar (matriz, mapa_matriz, x, y, ahogado, escapado);
  3138. }
  3139.  
  3140. void inicializar (int m[][DIMENSION], int mapa[][DIMENSION], int *px, int *py,
  3141.                   BOOLEAN *pahogado, BOOLEAN *pescapado,
  3142.                   int *pnumero_de_movimientos)
  3143. {
  3144.   register int i, j;
  3145.  
  3146.   for (i = 0; i < DIMENSION; i++)
  3147.     for (j = 0; j < DIMENSION; j++)
  3148.       m[i][j] = mapa[i][j] = 0;
  3149.  
  3150.   for (i = 0; i < DIMENSION; i++)
  3151.     m[0][i] = m[DIMENSION-1][i] = m[i][0] = m[i][DIMENSION-1] = -1;
  3152.  
  3153.   m[0][random (DIMENSION)] = m[DIMENSION-1][random (DIMENSION)] =
  3154.     m[random (DIMENSION)][0] = m[random (DIMENSION)][DIMENSION-1] = 1;
  3155.  
  3156.   *pahogado = *pescapado = FALSE;
  3157.   *pnumero_de_movimientos = 0;
  3158.  
  3159.   *px = random (DIMENSION);
  3160.   *py = random (DIMENSION);
  3161.  
  3162.   comprobar (m, mapa, *px, *py, pahogado, pescapado, pnumero_de_movimientos);
  3163. }
  3164.  
  3165. void mover (int movimiento, int *px, int *py)
  3166. {
  3167.   switch (movimiento)
  3168.     {
  3169.       case 1: --*px; break;
  3170.       case 2: --*py; break;
  3171.       case 3: ++*px; break;
  3172.       case 4: ++*py; break;
  3173.     }
  3174. }
  3175.  
  3176. void comprobar (int m[][DIMENSION], int mapa[][DIMENSION], int x, int y,
  3177.                 BOOLEAN *pahogado, BOOLEAN *pescapado,
  3178.                 int *pnumero_de_movimientos)
  3179. {
  3180.   if (m[x][y] == -1)
  3181.     *pahogado = TRUE;
  3182.   else if (m[x][y] == 1)
  3183.     *pescapado = TRUE;
  3184.   ++mapa[x][y];
  3185.   ++*pnumero_de_movimientos;
  3186. }
  3187.  
  3188. void finalizar (int m[][DIMENSION], int mapa[][DIMENSION], int x, int y,
  3189.                 BOOLEAN ahog, BOOLEAN escap)
  3190. {
  3191.   register int i, j;
  3192.  
  3193.   printf ("Matriz:\n");
  3194.   printf ("(#: agua; : salida; *: ha pasado el ratón; .: no ha pasado el ratón)\n\n");
  3195.   for (i = 0; i < DIMENSION; i++)
  3196.     {
  3197.       for (j = 0; j < DIMENSION; j++)
  3198.         printf ("%3c", m[i][j] == -1 ? '#' : m[i][j] == 1 ? '' :
  3199.                 mapa[i][j] == 0 ? '.' : '*');
  3200.       printf ("\n");
  3201.     }
  3202.  
  3203.   printf ("\nPulsa una tecla para continuar.");
  3204.   getch ();
  3205.  
  3206.   puts ("\nMapa:\n");
  3207.   for (i = 0; i < DIMENSION; i++)
  3208.     {
  3209.       for (j = 0; j < DIMENSION; j++)
  3210.         printf ("%3d", mapa[i][j]);
  3211.       printf ("\n");
  3212.     }
  3213.  
  3214.   printf ("\nPosición final: (%d, %d): ", x, y);
  3215.  
  3216.   if (escap)
  3217.     printf ("RATON SALVADO.\n");
  3218.   else if (ahog)
  3219.     printf ("RATON AHOGADO.\n");
  3220.   else
  3221.     printf ("RATON MUERTO DE HAMBRE.\n");
  3222.  
  3223.   printf ("Pulsa una tecla para finalizar.");
  3224.   getch ();
  3225. }
  3226. ende
  3227. begine " 32 FUNCIONES DE MANIPULACION DE ARRAYS DE CARACTERES "
  3228. /*
  3229.   Este programa consta de 32 funciones sobre manipulación de cadenas (arrays
  3230.   de caracteres) y la función main para probar las funciones mencionadas.
  3231.  
  3232.   Hay dos cosas importantes que comentar:
  3233.  
  3234.     1) Función borrar_pantalla. Hay dos versiones de esta función:
  3235.  
  3236.       - Para los usuarios de Turbo C, la función borrar pantalla es clrscr
  3237.         (clear screen) cuyo prototipo se encuentra en el fichero stdlib.h:
  3238.           void clrscr (void);
  3239.  
  3240.       - Para los usuarios que compilen este programa en otro compilador, se
  3241.         define la función completa utilizando la función int86() que se encuentra
  3242.         en la librería <dos.h>. Tanto la librería <dos.h> como la función
  3243.         int86() como el contenido de nuestra función borrar_pantalla() se
  3244.         explicará en lecciones posteriores. Lo único que tiene que saber el
  3245.         usuario, en este momento, es que la función borrar_pantalla borra la
  3246.         pantalla de texto actual como su propio nombre indica, y no importa
  3247.         ahora mismo cómo está implementada.
  3248.  
  3249.        La macro predefinida __TURBOC__ sólo está definida en los compiladores
  3250.        de Turbo C, por lo tanto, la expresión
  3251.          #ifdef __TURBOC__
  3252.        será cierta para los preprocesadores de Turbo C y falsa (se ejecuta
  3253.        el #else en este caso) para aquellos preprocesadores que no son de
  3254.        Turbo C.
  3255.  
  3256.     2) Funciones calloc y free. Estas dos funciones se encuentran en la
  3257.       librería <alloc.h>. La primera función asigna memoria y la segunda
  3258.       libera la memoria asignada. Las dos funciones se explicarán en
  3259.       lecciones posteriores; lo único que necesitas saber en este momento
  3260.       para comprender el programa es que calloc se utiliza de la siguiente
  3261.       forma en nuestro ejemplo:
  3262.  
  3263.         {
  3264.           char *p;
  3265.           p = (char *) calloc (1, 10);
  3266.           if (p == 0)
  3267.             error ();
  3268.           /* ahora se puede utilizar p como un array: p[indice] */
  3269.           /* ... */
  3270.           free (p); /* aquí se libera memoria asignada para p */
  3271.         }
  3272.  
  3273.       Las sentencias anteriores son, a efectos prácticos, equivalentes a:
  3274.  
  3275.         {
  3276.           char p[10];
  3277.           /* ... */
  3278.         } /* aquí se libera memoria asinada para p */
  3279.  
  3280.       En el primer caso, la cantidad de memoria a reservar se calcula en
  3281.       tiempo de ejecución y en el segundo se calcula en tiempo de compilación.
  3282.  
  3283.       Insisto, en este momento he explicado lo mínimo que hay que saber sobre
  3284.       las funciones calloc() y free() para entender el ejemplo. En lecciones
  3285.       ulteriores se estudiarán estas funciones en detalle.
  3286. */
  3287.  
  3288. /* Ficheros a incluir: */
  3289.  
  3290. #include <stdio.h> /* printf (), puts (), scanf (), gets () */
  3291. #include <alloc.h> /* calloc(), free () */ /* ver observación 2 al principio */
  3292. #include <conio.h> /* clrscr () si TURBO C, putch (), getch () */
  3293. #include <dos.h>   /* int86 (), union REGS */ /* ver obsev. 1 al principio */
  3294.  
  3295. /* Compilación condicional: */
  3296.  
  3297. #ifdef __TURBOC__ /* ver observación 1 en comentario inicial */
  3298.   #define borrar_pantalla clrscr
  3299. #else
  3300.   void borrar_pantalla (void) /* sólo hay que saber de esta función, en este momento, ... */
  3301.   {                           /* ... que borra la pantalla, no qué significa estas sentecias */
  3302.     union REGS regs;
  3303.     regs.h.ah = 6; /* código de desplazamiento de la pantalla */
  3304.     regs.h.al = 0; /* código de borrar la pantalla */
  3305.     regs.h.ch = 0; /* fila inicial */
  3306.     regs.h.cl = 0; /* columna inicial */
  3307.     regs.h.dh = 24; /* fila final */
  3308.     regs.h.dl = 79; /* columna final */
  3309.     regs.h.bh = 7; /* la línea de borrado en negra */
  3310.     int86 (0x10, ®s, ®s);
  3311.     regs.h.ah = 2; /* función de direccionamiento del cursor */
  3312.     regs.h.dl = 0; /* coordenada de la columna */
  3313.     regs.h.dh = 0; /* coordenada de la fila */
  3314.     regs.h.bh = 0; /* página de vídeo */
  3315.     int86 (0x10, ®s, ®s);
  3316.   }
  3317. #endif
  3318.  
  3319. /* Prototipos de funciones utilizadas: */
  3320.  
  3321. int longitud_de_cadena (char cadena[]);
  3322. void copiar_cadena (char cadena_fuente[], char cadena_destino[]);
  3323. int copiar_cadena_con_chequeo_de_indice (char cadena_fuente[],
  3324.                      char cadena_destino[], int numero_maximo_de_caracteres);
  3325. void copiar_cadena_rapido (char cadena_fuente[], char cadena_destino[]);
  3326. int copiar_cadena_rapido_con_chequeo_de_indice (char cadena_fuente[],
  3327.                      char cadena_destino[], int numero_maximo_de_caracteres);
  3328. void aniadir_cadena (char cadena_fuente[], char cadena_destino[]);
  3329. int insertar_cadena_con_chequeo_de_indice (char cadena_fuente[],
  3330.   char cadena_destino[], int indice_de_comienzo, int numero_maximo_de_caracteres);
  3331. int insertar_cadena (char cadena_fuente[], char cadena_destino[],
  3332.                      int indice_de_comienzo);
  3333. void cadena_a_mayuscula (char cadena[]);
  3334. void cadena_a_minuscula (char cadena[]);
  3335. void invertir_cadena_rapido (char cadena[]);
  3336. int invertir_cadena (char cadena[]);
  3337. void intercambiar_cadenas_rapido (char cadena1[], char cadena2[]);
  3338. int intercambiar_cadenas (char cadena1[], char cadena2[],
  3339.                           int tamanio1, int tamanio2);
  3340. int rellenar_cadena_rapido (char cadena[], int numero_de_blancos);
  3341. int rellenar_cadena (char cadena[], int numero_de_blancos,
  3342.                      int numero_maximo_de_caracteres);
  3343. int contar_caracter_en_cadena (char cadena[], char caracter);
  3344. int quitar_caracter_de_cadena (char cadena[], char caracter);
  3345. int indice_de_caracter_en_cadena (char cadena[], char caracter);
  3346. int indice_de_caracter_mas_a_la_derecha_en_cadena (char cadena[], char caracter);
  3347. void reemplazar_caracter_en_cadena (char cadena[], char caracter_fuente,
  3348.                                     char caracter_destino);
  3349. int llenar_cadena (char cadena[], int letra, int contador,
  3350.                    int numero_maximo_de_caracteres);
  3351. int primer_caracter_no_blanco_en_cadena (char cadena[]);
  3352. int ultimo_caracter_no_blanco_en_cadena (char cadena[]);
  3353. int cadenas_iguales (char cadena1[], char cadena2[], int ignorar_caso);
  3354. int primera_diferencia_de_cadenas (char cadena1[], char cadena2[],
  3355.                                    int ignorar_caso);
  3356. int comparar_cadenas (char cadena1[], char cadena2[], int ignorar_caso);
  3357. int indice_subcadena_en_cadena (char subcadena[], char cadena[]);
  3358. int contador_de_subcadenas_en_cadena (char subcadena[], char cadena[]);
  3359. int quitar_subcadena_de_cadena (char subcadena[], char cadena[]);
  3360. int indice_proxima_ocurrencia_de_subcadena_en_cadena (char subcadena[],
  3361.                                                       char cadena[], int indice);
  3362. int indice_patron_en_cadena (char subcadena[], char cadena[]);
  3363.  
  3364. /* Definición de funciones: */
  3365.  
  3366. /*
  3367.   Función principal.
  3368. */
  3369.  
  3370. void main (void)
  3371. {
  3372.   #define ESC 27
  3373.   #define CARACTER_A_MAYUSCULA(c) ((c) >= 'a' && (c) <= 'z' ? ((c) & ~32) : (c))
  3374.   #define ENTRE(x,x1,x2) ((x) >= (x1) && (x) <= (x2))
  3375.  
  3376.   #define escribir_titulo(s) printf ("FUNCION:\n\n%s\n", s)
  3377.   #define leer_cadena1() { printf ("\ncadena1 (35 caracteres máximo): "); \
  3378.                            scanf ("%35s", cadena1); }
  3379.   #define leer_cadena2() { printf ("\ncadena2 (35 caracteres máximo): "); \
  3380.                            scanf ("%35s", cadena2); }
  3381.   #define leer_numero_maximo_de_caracteres() { printf ("\nnumero_maximo_de_caracteres: "); \
  3382.                                                scanf ("%d", &numero_maximo_de_caracteres); }
  3383.   #define leer_valor_entero(nombre_variable, variable) { printf ("\n%s: ", nombre_variable); \
  3384.                                                          scanf ("%d", &variable); }
  3385.   #define leer_caracter1() { printf ("\ncaracter1: "); caracter1 = getche (); putch ('\n'); }
  3386.   #define leer_caracter2() { printf ("\ncaracter2: "); caracter2 = getche (); putch ('\n'); }
  3387.   #define escribir_cadena1() printf ("\ncadena1: %-35s\n", cadena1);
  3388.   #define escribir_cadena2() printf ("\ncadena2: %-35s\n", cadena2);
  3389.   #define escribir_valor_devuelto() printf ("\nvalor_devuelto: %d\n", valor_devuelto);
  3390.   #define escribir_entrada() printf ("\n\nVALORES DE ENTRADA:\n");
  3391.   #define escribir_salida() printf ("\n\nVALORES DE SALIDA:\n");
  3392.  
  3393.   char ch1, ch2;
  3394.   register int i;
  3395.   int numero_de_letras, numero_de_cifras;
  3396.   char cadena1[35+1], cadena2[35+1]; /* +1 para guardar el nulo */
  3397.   int numero_maximo_de_caracteres, valor_devuelto;
  3398.   char caracter1, caracter2;
  3399.   char *funciones[] =
  3400.     {
  3401.       "longitud_de_cadena",
  3402.       "copiar_cadena",
  3403.       "copiar_cadena_con_chequeo_de_indice",
  3404.       "copiar_cadena_rapido",
  3405.       "copiar_cadena_rapido_con_chequeo_de_indice",
  3406.       "aniadir_cadena",
  3407.       "insertar_cadena_con_chequeo_de_indice",
  3408.       "insertar_cadena",
  3409.       "cadena_a_mayuscula",
  3410.       "cadena_a_minuscula",
  3411.       "invertir_cadena_rapido",
  3412.       "invertir_cadena",
  3413.       "intercambiar_cadenas_rapido",
  3414.       "intercambiar_cadenas",
  3415.       "rellenar_cadena_rapido",
  3416.       "rellenar_cadena",
  3417.       "contar_caracter_en_cadena",
  3418.       "quitar_caracter_de_cadena",
  3419.       "indice_de_caracter_en_cadena",
  3420.       "indice_de_caracter_mas_a_la_derecha_en_cadena",
  3421.       "reemplazar_caracter_en_cadena",
  3422.       "llenar_cadena",
  3423.       "primer_caracter_no_blanco_en_cadena",
  3424.       "ultimo_caracter_no_blanco_en_cadena",
  3425.       "cadenas_iguales",
  3426.       "primera_diferencia_de_cadenas",
  3427.       "comparar_cadenas",
  3428.       "indice_subcadena_en_cadena",
  3429.       "contador_de_subcadenas_en_cadena",
  3430.       "quitar_subcadena_de_cadena",
  3431.       "indice_proxima_ocurrencia_de_subcadena_en_cadena",
  3432.       "indice_patron_en_cadena",
  3433.       NULL
  3434.     };
  3435.  
  3436.   do
  3437.     {
  3438.       borrar_pantalla ();
  3439.  
  3440.       printf ("FUNCIONES PARA PROBAR:\n\n");
  3441.       for (i = 0; funciones[i] != NULL; i++)
  3442.         {
  3443.           printf ("%c.- %-35.35s", 'A' + i > 'Z' ? '0' + i - ('Z' - 'A' + 1) :
  3444.                    'A' + i, funciones[i]);
  3445.           if (funciones[i+1] != NULL)
  3446.             {
  3447.               i++;
  3448.               printf (" %c.- %-35.35s\n", 'A' + i > 'Z' ? '0' + i - ('Z' - 'A' + 1) :
  3449.                        'A' + i, funciones[i]);
  3450.             }
  3451.           else
  3452.             printf ("\n");
  3453.         }
  3454.  
  3455.       numero_de_letras = i;
  3456.       numero_de_cifras = numero_de_letras > 'Z' - 'A' + 1 ? i - ('Z' - 'A' + 1) : 0;
  3457.       printf ("\nTecla letra correspondiente a función a probar (ESC para salir): ");
  3458.  
  3459.       do
  3460.         {
  3461.           ch1 = getch (); /* lee carácter correspondiente a opción */
  3462.           ch1 = CARACTER_A_MAYUSCULA (ch1); /* convierte carácter a mayúscula */
  3463.         } while (ch1 != ESC && ! ENTRE (ch1, 'A', 'A' + numero_de_letras - 1) &&
  3464.                  (numero_de_cifras >= 0 && ! ENTRE (ch1, '0', '0' + numero_de_cifras - 1)));
  3465.  
  3466.       if (ch1 != ESC)
  3467.         do
  3468.           {
  3469.             borrar_pantalla ();
  3470.  
  3471.             switch (ch1)
  3472.               {
  3473.                 case 'A':
  3474.                   escribir_titulo ("int longitud_de_cadena (char cadena[]);");
  3475.                   escribir_entrada ();
  3476.                   leer_cadena1 ();
  3477.                   valor_devuelto = longitud_de_cadena (cadena1);
  3478.                   escribir_salida ();
  3479.                   escribir_valor_devuelto ();
  3480.                   break;
  3481.  
  3482.                 case 'B':
  3483.                   escribir_titulo ("void copiar_cadena (char cadena_fuente[], char cadena_destino[]);");
  3484.                   escribir_entrada ();
  3485.                   leer_cadena1 ();
  3486.                   copiar_cadena (cadena1, cadena2);
  3487.                   escribir_salida ();
  3488.                   escribir_cadena2 ();
  3489.                   break;
  3490.  
  3491.                 case 'C':
  3492.                   escribir_titulo ("int copiar_cadena_con_chequeo_de_indice (char cadena_fuente[],\n"
  3493.                                    "\t\tchar cadena_destino[], int numero_maximo_de_caracteres);");
  3494.                   escribir_entrada ();
  3495.                   leer_cadena1 ();
  3496.                   leer_numero_maximo_de_caracteres ();
  3497.                   valor_devuelto = copiar_cadena_con_chequeo_de_indice (cadena1,
  3498.                                        cadena2, numero_maximo_de_caracteres);
  3499.                   escribir_salida ();
  3500.                   escribir_cadena2 ();
  3501.                   escribir_valor_devuelto ();
  3502.                   break;
  3503.  
  3504.                 case 'D':
  3505.                   escribir_titulo ("void copiar_cadena_rapido (char cadena_fuente[], char cadena_destino[]);");
  3506.                   escribir_entrada ();
  3507.                   leer_cadena1 ();
  3508.                   copiar_cadena (cadena1, cadena2);
  3509.                   escribir_salida ();
  3510.                   escribir_cadena2 ();
  3511.                   break;
  3512.  
  3513.                 case 'E':
  3514.                   escribir_titulo ("int copiar_cadena_rapido_con_chequeo_de_indice (char cadena_fuente[],\n"
  3515.                                    "\t\tchar cadena_destino[], int numero_maximo_de_caracteres);");
  3516.                   escribir_entrada ();
  3517.                   leer_cadena1 ();
  3518.                   leer_numero_maximo_de_caracteres ();
  3519.                   valor_devuelto = copiar_cadena_con_chequeo_de_indice (cadena1,
  3520.                                        cadena2, numero_maximo_de_caracteres);
  3521.                   escribir_salida ();
  3522.                   escribir_cadena2 ();
  3523.                   escribir_valor_devuelto ();
  3524.                   break;
  3525.  
  3526.                 case 'F':
  3527.                   escribir_titulo ("void aniadir_cadena (char cadena_fuente[], char cadena_destino[]);");
  3528.                   escribir_entrada ();
  3529.                   leer_cadena1 ();
  3530.                   leer_cadena2 ();
  3531.                   aniadir_cadena (cadena1, cadena2);
  3532.                   escribir_salida ();
  3533.                   escribir_cadena2 ();
  3534.                   break;
  3535.  
  3536.                 case 'G':
  3537.                   {
  3538.                     int indice_de_comienzo;
  3539.                     escribir_titulo ("int insertar_cadena_con_chequeo_de_indice (char cadena_fuente[], char\n"
  3540.                                      "  cadena_destino[], int indice_de_comienzo, int numero_maximo_de_caracteres);");
  3541.                     escribir_entrada ();
  3542.                     leer_cadena1 ();
  3543.                     leer_cadena2 ();
  3544.                     leer_valor_entero ("indice_de_comienzo", indice_de_comienzo);
  3545.                     leer_numero_maximo_de_caracteres ();
  3546.                     valor_devuelto = insertar_cadena_con_chequeo_de_indice (cadena1, cadena2,
  3547.                                         indice_de_comienzo, numero_maximo_de_caracteres);
  3548.                     escribir_salida ();
  3549.                     escribir_cadena2 ();
  3550.                     escribir_valor_devuelto ();
  3551.                   }
  3552.                   break;
  3553.  
  3554.                 case 'H':
  3555.                   {
  3556.                     int indice_de_comienzo;
  3557.                     escribir_titulo ("int insertar_cadena (char cadena_fuente[], char cadena_destino[],\n"
  3558.                                      "                     int indice_de_comienzo);");
  3559.                     escribir_entrada ();
  3560.                     leer_cadena1 ();
  3561.                     leer_cadena2 ();
  3562.                     leer_valor_entero ("indice_de_comienzo", indice_de_comienzo);
  3563.                     valor_devuelto = insertar_cadena (cadena1, cadena2, indice_de_comienzo);
  3564.                     escribir_salida ();
  3565.                     escribir_cadena2 ();
  3566.                     escribir_valor_devuelto ();
  3567.                   }
  3568.                   break;
  3569.  
  3570.                 case 'I':
  3571.                   escribir_titulo ("void cadena_a_mayuscula (char cadena[]);");
  3572.                   escribir_entrada ();
  3573.                   leer_cadena1 ();
  3574.                   cadena_a_mayuscula (cadena1);
  3575.                   escribir_salida ();
  3576.                   escribir_cadena1 ();
  3577.                   break;
  3578.  
  3579.                 case 'J':
  3580.                   escribir_titulo ("void cadena_a_minuscula (char cadena[]);");
  3581.                   escribir_entrada ();
  3582.                   leer_cadena1 ();
  3583.                   cadena_a_minuscula (cadena1);
  3584.                   escribir_salida ();
  3585.                   escribir_cadena1 ();
  3586.                   break;
  3587.  
  3588.                 case 'K':
  3589.                   escribir_titulo ("void invertir_cadena_rapido (char cadena[]);");
  3590.                   escribir_entrada ();
  3591.                   leer_cadena1 ();
  3592.                   invertir_cadena_rapido (cadena1);
  3593.                   escribir_salida ();
  3594.                   escribir_cadena1 ();
  3595.                   break;
  3596.  
  3597.                 case 'L':
  3598.                   escribir_titulo ("int invertir_cadena (char cadena[]);");
  3599.                   escribir_entrada ();
  3600.                   leer_cadena1 ();
  3601.                   valor_devuelto = invertir_cadena (cadena1);
  3602.                   escribir_salida ();
  3603.                   escribir_cadena1 ();
  3604.                   escribir_valor_devuelto ();
  3605.                   break;
  3606.  
  3607.                 case 'M':
  3608.                   escribir_titulo ("void intercambiar_cadenas_rapido (char cadena1[], char cadena2[]);");
  3609.                   escribir_entrada ();
  3610.                   leer_cadena1 ();
  3611.                   leer_cadena2 ();
  3612.                   intercambiar_cadenas_rapido (cadena1, cadena2);
  3613.                   escribir_salida ();
  3614.                   escribir_cadena1 ();
  3615.                   escribir_cadena2 ();
  3616.                   break;
  3617.  
  3618.                 case 'N':
  3619.                   {
  3620.                     int tamanio1, tamanio2;
  3621.                     escribir_titulo ("int intercambiar_cadenas (char cadena1[], char cadena2[],\n"
  3622.                                      "                          int tamanio1, int tamanio2);");
  3623.                     escribir_entrada ();
  3624.                     leer_cadena1 ();
  3625.                     leer_cadena2 ();
  3626.                     leer_valor_entero ("tamanio1", tamanio1);
  3627.                     leer_valor_entero ("tamanio2", tamanio2);
  3628.                     valor_devuelto = intercambiar_cadenas (cadena1, cadena2,
  3629.                                                            tamanio1, tamanio2);
  3630.                     escribir_salida ();
  3631.                     escribir_cadena1 ();
  3632.                     escribir_cadena2 ();
  3633.                     escribir_valor_devuelto ();
  3634.                   }
  3635.                   break;
  3636.  
  3637.                 case 'O':
  3638.                   {
  3639.                     int numero_de_blancos;
  3640.                     escribir_titulo ("int rellenar_cadena_rapido (char cadena[], int numero_de_blancos);");
  3641.                     escribir_entrada ();
  3642.                     leer_cadena1 ();
  3643.                     leer_valor_entero ("numero_de_blancos", numero_de_blancos);
  3644.                     valor_devuelto = rellenar_cadena_rapido (cadena1, numero_de_blancos);
  3645.                     escribir_salida ();
  3646.                     escribir_cadena1 ();
  3647.                     escribir_valor_devuelto ();
  3648.                   }
  3649.                   break;
  3650.  
  3651.                 case 'P':
  3652.                   {
  3653.                     int numero_de_blancos;
  3654.                     escribir_titulo ("int rellenar_cadena (char cadena[], int numero_de_blancos,\n"
  3655.                                      "                     int numero_maximo_de_caracteres);");
  3656.                     escribir_entrada ();
  3657.                     leer_cadena1 ();
  3658.                     leer_valor_entero ("numero_de_blancos", numero_de_blancos);
  3659.                     valor_devuelto = rellenar_cadena (cadena1, numero_de_blancos,
  3660.                                                       numero_maximo_de_caracteres);
  3661.                     escribir_salida ();
  3662.                     escribir_cadena1 ();
  3663.                     escribir_valor_devuelto ();
  3664.                   }
  3665.                   break;
  3666.  
  3667.                 case 'Q':
  3668.                   escribir_titulo ("int contar_caracter_en_cadena (char cadena[], char caracter);");
  3669.                   escribir_entrada ();
  3670.                   leer_cadena1 ();
  3671.                   leer_caracter1 ();
  3672.                   valor_devuelto = contar_caracter_en_cadena (cadena1, caracter1);
  3673.                   escribir_salida ();
  3674.                   escribir_valor_devuelto ();
  3675.                   break;
  3676.  
  3677.                 case 'R':
  3678.                   escribir_titulo ("int quitar_caracter_de_cadena (char cadena[], char caracter1);");
  3679.                   escribir_entrada ();
  3680.                   leer_cadena1 ();
  3681.                   leer_caracter1 ();
  3682.                   valor_devuelto = quitar_caracter_de_cadena (cadena1, caracter1);
  3683.                   escribir_salida ();
  3684.                   escribir_cadena1 ();
  3685.                   escribir_valor_devuelto ();
  3686.                   break;
  3687.  
  3688.                 case 'S':
  3689.                   escribir_titulo ("int indice_de_caracter_en_cadena (char cadena[], char caracter1);");
  3690.                   escribir_entrada ();
  3691.                   leer_cadena1 ();
  3692.                   leer_caracter1 ();
  3693.                   valor_devuelto = indice_de_caracter_en_cadena (cadena1, caracter1);
  3694.                   escribir_salida ();
  3695.                   escribir_valor_devuelto ();
  3696.                   break;
  3697.  
  3698.                 case 'T':
  3699.                   escribir_titulo ("int indice_de_caracter_mas_a_la_derecha_en_cadena (char cadena[], char caracter1);");
  3700.                   escribir_entrada ();
  3701.                   leer_cadena1 ();
  3702.                   leer_caracter1 ();
  3703.                   valor_devuelto = indice_de_caracter_en_cadena (cadena1, caracter1);
  3704.                   escribir_salida ();
  3705.                   escribir_valor_devuelto ();
  3706.                   break;
  3707.  
  3708.                 case 'U':
  3709.                   escribir_titulo ("void reemplazar_caracter_en_cadena (char cadena[], char caracter_fuente,\n"
  3710.                                    "                                    char caracter_destino);");
  3711.                   escribir_entrada ();
  3712.                   leer_cadena1 ();
  3713.                   leer_caracter1 ();
  3714.                   leer_caracter2 ();
  3715.                   reemplazar_caracter_en_cadena (cadena1, caracter1, caracter2);
  3716.                   escribir_salida ();
  3717.                   escribir_cadena1 ();
  3718.                   break;
  3719.  
  3720.                 case 'V':
  3721.                   {
  3722.                     int contador;
  3723.                     escribir_titulo ("int llenar_cadena (char cadena[], int caracter, int contador,\n"
  3724.                                      "                   int numero_maximo_de_caracteres);");
  3725.                     escribir_entrada ();
  3726.                     leer_caracter1 ();
  3727.                     leer_valor_entero ("contador", contador);
  3728.                     leer_numero_maximo_de_caracteres ();
  3729.                     valor_devuelto = llenar_cadena (cadena1, caracter1, contador,
  3730.                                                     numero_maximo_de_caracteres);
  3731.                     escribir_salida ();
  3732.                     escribir_cadena1 ();
  3733.                     escribir_valor_devuelto ();
  3734.                   }
  3735.                   break;
  3736.  
  3737.                 case 'W':
  3738.                   escribir_titulo ("int primer_caracter_no_blanco_en_cadena (char cadena[]);");
  3739.                   escribir_entrada ();
  3740.                   printf ("\ncadena 1: "); gets (cadena1); /* scanf no lee los primeros blancos */
  3741.                   valor_devuelto = primer_caracter_no_blanco_en_cadena (cadena1);
  3742.                   escribir_salida ();
  3743.                   escribir_valor_devuelto ();
  3744.                   break;
  3745.  
  3746.                 case 'X': /* REVISAR */
  3747.                   escribir_titulo ("int ultimo_caracter_no_blanco_en_cadena (char cadena[]);");
  3748.                   escribir_entrada ();
  3749.                   printf ("\ncadena 1: "); gets (cadena1); /* scanf no lee los últimos blancos */
  3750.                   valor_devuelto = ultimo_caracter_no_blanco_en_cadena (cadena1);
  3751.                   escribir_salida ();
  3752.                   escribir_valor_devuelto ();
  3753.                   break;
  3754.  
  3755.                 case 'Y':
  3756.                   {
  3757.                     int ignorar_caso;
  3758.                     escribir_titulo ("int cadenas_iguales (char cadena1[], char cadena2[], int ignorar_caso);");
  3759.                     escribir_entrada ();
  3760.                     leer_cadena1 ();
  3761.                     leer_cadena2 ();
  3762.                     leer_valor_entero ("ignorar_caso", ignorar_caso);
  3763.                     valor_devuelto = cadenas_iguales (cadena1, cadena2, ignorar_caso);
  3764.                     escribir_salida ();
  3765.                     escribir_valor_devuelto ();
  3766.                   }
  3767.                   break;
  3768.  
  3769.                 case 'Z':
  3770.                   {
  3771.                     int ignorar_caso;
  3772.                     escribir_titulo ("int primera_diferencia_de_cadenas (char cadena1[], char cadena2[],\n"
  3773.                                      "                                   int ignorar_caso);");
  3774.                     escribir_entrada ();
  3775.                     leer_cadena1 ();
  3776.                     leer_cadena2 ();
  3777.                     leer_valor_entero ("ignorar_caso", ignorar_caso);
  3778.                     valor_devuelto = primera_diferencia_de_cadenas (cadena1, cadena2, ignorar_caso);
  3779.                     escribir_salida ();
  3780.                     escribir_valor_devuelto ();
  3781.                   }
  3782.                   break;
  3783.  
  3784.                 case '0':
  3785.                   {
  3786.                     int ignorar_caso;
  3787.                     escribir_titulo ("int comparar_cadenas (char cadena1[], char cadena2[], int ignorar_caso);");
  3788.                     escribir_entrada ();
  3789.                     leer_cadena1 ();
  3790.                     leer_cadena2 ();
  3791.                     leer_valor_entero ("ignorar_caso", ignorar_caso);
  3792.                     valor_devuelto = comparar_cadenas (cadena1, cadena2, ignorar_caso);
  3793.                     escribir_salida ();
  3794.                     escribir_valor_devuelto ();
  3795.                   }
  3796.                   break;
  3797.  
  3798.                 case '1':
  3799.                   escribir_titulo ("int indice_subcadena_en_cadena (char subcadena[], char cadena[]);");
  3800.                   escribir_entrada ();
  3801.                   leer_cadena1 ();
  3802.                   leer_cadena2 ();
  3803.                   valor_devuelto = indice_subcadena_en_cadena (cadena1, cadena2);
  3804.                   escribir_salida ();
  3805.                   escribir_valor_devuelto ();
  3806.                   break;
  3807.  
  3808.                 case '2':
  3809.                   escribir_titulo ("int contador_de_subcadenas_en_cadena (char subcadena[], char cadena[]);");
  3810.                   escribir_entrada ();
  3811.                   leer_cadena1 ();
  3812.                   leer_cadena2 ();
  3813.                   valor_devuelto = contador_de_subcadenas_en_cadena (cadena1, cadena2);
  3814.                   escribir_salida ();
  3815.                   escribir_valor_devuelto ();
  3816.                   break;
  3817.  
  3818.                 case '3':
  3819.                   escribir_titulo ("int quitar_subcadena_de_cadena (char subcadena[], char cadena[]);");
  3820.                   escribir_entrada ();
  3821.                   leer_cadena1 ();
  3822.                   leer_cadena2 ();
  3823.                   valor_devuelto = quitar_subcadena_de_cadena (cadena1, cadena2);
  3824.                   escribir_salida ();
  3825.                   escribir_cadena1 ();
  3826.                   escribir_valor_devuelto ();
  3827.                   break;
  3828.  
  3829.                 case '4':
  3830.                   {
  3831.                     int indice;
  3832.                     escribir_titulo ("int indice_proxima_ocurrencia_de_subcadena_en_cadena (char subcadena[],\n"
  3833.                                      "\t\tchar cadena[], int indice);");
  3834.                     escribir_entrada ();
  3835.                     leer_cadena1 ();
  3836.                     leer_cadena2 ();
  3837.                     leer_valor_entero ("indice", indice);
  3838.                     valor_devuelto = comparar_cadenas (cadena1, cadena2, indice);
  3839.                     escribir_salida ();
  3840.                     escribir_valor_devuelto ();
  3841.                   }
  3842.                   break;
  3843.  
  3844.                 case '5':
  3845.                   escribir_titulo ("int indice_patron_en_cadena (char subcadena[], char cadena[]);");
  3846.                   escribir_entrada ();
  3847.                   leer_cadena1 ();
  3848.                   leer_cadena2 ();
  3849.                   valor_devuelto = indice_patron_en_cadena (cadena1, cadena2);
  3850.                   escribir_salida ();
  3851.                   escribir_valor_devuelto ();
  3852.                   break;
  3853.               }
  3854.  
  3855.            while (kbhit ()) /* vacía buffer de teclas */
  3856.              getch ();
  3857.  
  3858.            do
  3859.              {
  3860.                printf ("\n\n\n¿Deseas probar otra vez esta función (S/N)? ");
  3861.                ch2 = getch ();
  3862.                ch2 = CARACTER_A_MAYUSCULA (ch2);
  3863.              } while (ch2 != 'S' && ch2 != 'N');
  3864.  
  3865.            while (kbhit ()) /* vacía buffer de teclas */
  3866.              getch ();
  3867.  
  3868.           } while (ch2 == 'S');
  3869.  
  3870.     } while (ch1 != ESC);
  3871. }
  3872.  
  3873. /*
  3874.   Devuelve el número de caracteres in cadena
  3875. */
  3876.  
  3877. int longitud_de_cadena (char cadena[])
  3878. {
  3879.   register int i;
  3880.  
  3881.   for (i = 0; cadena[i]; i++)
  3882.     ;
  3883.  
  3884.   return (i);
  3885. }
  3886.  
  3887. /*
  3888.   Copia el contenido de la cadena fuente a la cadena destino.
  3889.   No chequea índices. Esto quiere decir que si hacemos:
  3890.  
  3891.   void main (void)
  3892.   {
  3893.     cadena[2];
  3894.     copiar_cadena ("hola", cadena);
  3895.   }
  3896.  
  3897.   escribimos los caractes 'l' y 'a' en lugares de memoria desconocidos.
  3898. */
  3899.  
  3900. void copiar_cadena (char cadena_fuente[], char cadena_destino[])
  3901. {
  3902.   register int i;
  3903.  
  3904.   for (i = 0; cadena_fuente[i] != '\0'; ++i)
  3905.     cadena_destino[i] = cadena_fuente[i];
  3906.  
  3907.   cadena_destino[i] = '\0';
  3908. }
  3909.  
  3910. /*
  3911.   Copia el contenido de la cadena fuente a la cadena destino.
  3912.  
  3913.   Devuelve 1 si cadena_destino no se ha podido copiar entera en cadena
  3914.   fuente; y 0 en caso contrario.
  3915.  
  3916.   Chequea índices. De esta forma podemos evitar errores desagradables
  3917.   como el ejemplo que se ha comentado en la función anterior.
  3918.  
  3919.   Ejemplo de utilización:
  3920.     estado = copiar_cadena_con_chequeo_de_indice ("cadena", variable_cadena,
  3921.                                                   sizeof (variable_cadena));
  3922. */
  3923.  
  3924. int copiar_cadena_con_chequeo_de_indice (char cadena_fuente[],
  3925.                      char cadena_destino[], int numero_maximo_de_caracteres)
  3926. {
  3927.   register int i;
  3928.  
  3929.   numero_maximo_de_caracteres--;
  3930.  
  3931.   for (i = 0; cadena_fuente[i] != '\0' && i < numero_maximo_de_caracteres; i++)
  3932.     cadena_destino[i] = cadena_fuente[i];
  3933.  
  3934.   cadena_destino[i] = '\0';
  3935.  
  3936.   return (cadena_fuente[i] && i == numero_maximo_de_caracteres);
  3937. }
  3938.  
  3939. /*
  3940.   Copia el contenido de la cadena fuente a la cadena destino.
  3941.  
  3942.   Hace exactamente lo mismo que la función copiar_cadena() pero esta versión
  3943.   es mucho más rápida.
  3944.  
  3945.   Se han hecho dos optimizaciones:
  3946.  
  3947.   1) El código:
  3948.  
  3949.     for (i = 0; cadena_fuente[i] != '\0'; ++i)
  3950.       cadena_destino[i] = cadena_fuente[i];
  3951.     cadena_destino[i] = '\0';
  3952.  
  3953.   es más eficiente así:
  3954.  
  3955.     for (i = 0; (cadena_destino[i] = cadena_fuente[i]) != '\0'; ++i)
  3956.       ;
  3957.  
  3958.   En la segunda versión, para cada testeo de la condición, se asigna un
  3959.   carácter de cadena_fuente a cadena_destino y después se comprueba si
  3960.   el carácter asignado es '\0', una vez que el carácter nulo ha sido
  3961.   asignado se termina el bucle.
  3962.  
  3963.   Todavía es más eficiente de la siguiente forma:
  3964.  
  3965.     for (i = 0; (cadena_destino[i] = cadena_fuente[i]); ++i)
  3966.       ;
  3967.  
  3968.   puesto que el carácter '\0' es equivalente al entero 0, y una expresión
  3969.   con valor 0 en C se considera falsa y una expresión con valor distinto de
  3970.   0 se considera verdadera.
  3971. */
  3972.  
  3973. void copiar_cadena_rapido (char cadena_fuente[], char cadena_destino[])
  3974. {
  3975.   register int i;
  3976.  
  3977.   for (i = 0; cadena_destino[i] = cadena_fuente[i]; i++)
  3978.     ;
  3979. }
  3980.  
  3981. /*
  3982.   Esta función hace lo mismo que la función copiar_cadena_con_chequeo_de_indice()
  3983.   pero con el código minimizado.
  3984.  
  3985.   La minimización se explica en el comentario de la función anterior.
  3986. */
  3987.  
  3988. int copiar_cadena_rapido_con_chequeo_de_indice (char cadena_fuente[],
  3989.                      char cadena_destino[], int numero_maximo_de_caracteres)
  3990. {
  3991.   register int i;
  3992.  
  3993.   numero_maximo_de_caracteres--;
  3994.  
  3995.   for (i = 0; (cadena_destino[i] = cadena_fuente[i]) &&
  3996.        i < numero_maximo_de_caracteres; i++)
  3997.     ;
  3998.  
  3999.   if (i == numero_maximo_de_caracteres && cadena_fuente[i])
  4000.     {
  4001.       cadena_destino[i] = '\0';
  4002.       return (1);
  4003.     }
  4004.   else
  4005.     return (0);
  4006. }
  4007.  
  4008. /*
  4009.   Añade el contenido de cadena_fuente a cadena_destino.
  4010.   No chequea índice.
  4011. */
  4012.  
  4013. void aniadir_cadena (char cadena_fuente[], char cadena_destino[])
  4014. {
  4015.   register int i, j;
  4016.  
  4017.   for (i = 0; cadena_destino[i]; i++) /* encuentra el final de cadena_destino */
  4018.     ;
  4019.  
  4020.   for (j = 0; cadena_destino[i] = cadena_fuente[j]; i++, j++) /* añade cadena_fuente */
  4021.     ;
  4022. }
  4023.  
  4024. /*
  4025.   Inserta el contenido de cadena_destino en cadena_fuente a partir de
  4026.   indice_de_comienzo.
  4027.  
  4028.   Los valores devueltos son:
  4029.     -1: memoria insuficiente.
  4030.      0: inserción con éxito.
  4031.      1: inserción incompleta.
  4032. */
  4033.  
  4034. int insertar_cadena_con_chequeo_de_indice (char cadena_fuente[],
  4035.   char cadena_destino[], int indice_de_comienzo, int numero_maximo_de_caracteres)
  4036. {
  4037.   register int i, j, longitud_cadena_fuente, longitud_cadena_destino;
  4038.  
  4039.   char *cadena_temporal;
  4040.  
  4041.   for (longitud_cadena_fuente = 0; cadena_fuente[longitud_cadena_fuente];
  4042.        ++longitud_cadena_fuente) /* obtiene longitud de cadena_fuente */
  4043.     ;
  4044.  
  4045.   for (longitud_cadena_destino = 0; cadena_fuente[longitud_cadena_destino];
  4046.        ++longitud_cadena_destino) /* obtiene longitud de cadena_destino */
  4047.     ;
  4048.  
  4049.   if (indice_de_comienzo > longitud_cadena_destino) /* añade */
  4050.     indice_de_comienzo = longitud_cadena_destino;
  4051.  
  4052.   if ((cadena_temporal = (char *) calloc (1, longitud_cadena_fuente +
  4053.                                           longitud_cadena_destino + 1)) == '\0')
  4054.     return (-1);
  4055.  
  4056.   for (i = 0; i < indice_de_comienzo; ++i)
  4057.     cadena_temporal[i] = cadena_destino[i];
  4058.  
  4059.   for (j = 0; cadena_temporal[i+j] = cadena_fuente[j]; j++)
  4060.     ;
  4061.  
  4062.   while (cadena_temporal[i+j] = cadena_destino[i])
  4063.     ++i;
  4064.  
  4065.   for (i = 0; (cadena_destino[i] = cadena_temporal[i]) &&
  4066.        i < numero_maximo_de_caracteres; i++)
  4067.     ;
  4068.  
  4069.   free (cadena_temporal);
  4070.  
  4071.   if (i == numero_maximo_de_caracteres && cadena_destino[i]) /* inserción está incompleta */
  4072.     {
  4073.       cadena_destino[i] = '\0';
  4074.       return (1);
  4075.     }
  4076.   else
  4077.     return (0);
  4078. }
  4079.  
  4080. /*
  4081.   Hace lo mismo que la función anterior pero no chequea índice. De esta forma
  4082.   es más rápida.
  4083.  
  4084.   Los valores devueltos son:
  4085.     -1: memoria insuficiente.
  4086.      0: inserción con éxito.
  4087. */
  4088.  
  4089. int insertar_cadena (char cadena_fuente[], char cadena_destino[],
  4090.                      int indice_de_comienzo)
  4091. {
  4092.   register int i, j, longitud_cadena_fuente, longitud_cadena_destino;
  4093.  
  4094.   char *cadena_temporal;
  4095.  
  4096.   for (longitud_cadena_fuente = 0; cadena_fuente[longitud_cadena_fuente];
  4097.        ++longitud_cadena_fuente) /* obtiene longitud de cadena_fuente */
  4098.     ;
  4099.  
  4100.   for (longitud_cadena_destino = 0; cadena_fuente[longitud_cadena_destino];
  4101.        ++longitud_cadena_destino) /* obtiene longitud de cadena_destino */
  4102.     ;
  4103.  
  4104.   if (indice_de_comienzo > longitud_cadena_destino) /* añade */
  4105.     indice_de_comienzo = longitud_cadena_destino;
  4106.  
  4107.   if ((cadena_temporal = (char *) calloc (1, longitud_cadena_fuente +
  4108.                                           longitud_cadena_destino + 1)) == '\0')
  4109.     return (-1);
  4110.  
  4111.   for (i = 0; i < indice_de_comienzo; ++i)
  4112.     cadena_temporal[i] = cadena_destino[i];
  4113.  
  4114.   for (j = 0; cadena_temporal[i+j] = cadena_fuente[j]; j++)
  4115.     ;
  4116.  
  4117.   while (cadena_temporal[i+j] = cadena_destino[i])
  4118.     ++i;
  4119.  
  4120.   for (i = 0; cadena_destino[i] = cadena_temporal[i]; i++)
  4121.     ;
  4122.  
  4123.   free (cadena_temporal);
  4124.  
  4125.   return (0);
  4126. }
  4127.  
  4128. /*
  4129.   Convierte una cadena a caracteres en mayúsculas.
  4130.  
  4131.   Nota: los caracteres ASCII mayores de 127 como la ñ y las vocales acentuadas
  4132.   no son convertida a mayúsculas. Sería muy fácil contemplar estos caracteres
  4133.   también.
  4134. */
  4135.  
  4136. void cadena_a_mayuscula (char cadena[])
  4137. {
  4138.   register int i;
  4139.  
  4140.   for (i = 0; cadena[i]; i++)
  4141.     if (cadena[i] >= 'a' && cadena[i] <= 'z')
  4142.       cadena[i] &= ~32;
  4143. }
  4144.  
  4145. /*
  4146.   Convierte una cadena a caracteres en minúsculas.
  4147.  
  4148.   Nota: los caracteres ASCII mayores de 127 como la ñ y las vocales acentuadas
  4149.   no son convertida a minúsculas. Sería muy fácil contemplar estos caracteres
  4150.   también.
  4151. */
  4152.  
  4153. void cadena_a_minuscula (char cadena[])
  4154. {
  4155.   register int i;
  4156.  
  4157.   for (i = 0; cadena[i]; i++)
  4158.     if (cadena[i] >= 'A' && cadena[i] <= 'Z')
  4159.       cadena[i] |= 32;
  4160. }
  4161.  
  4162. /*
  4163.   Invierte el contenido de cadena.
  4164.  
  4165.   Este método requiere (1.5 * n) intercambios, siendo n el número de
  4166.   elementos del array.
  4167. */
  4168.  
  4169. void invertir_cadena_rapido (char cadena[])
  4170. {
  4171.   char caracter_temporal;
  4172.  
  4173.   register int i, j;
  4174.  
  4175.   for (j = 0; cadena[j]; ++j) /* encuentra el final de cadena */
  4176.     ;
  4177.  
  4178.   for (i = 0, j--; i < j; i++, j--)
  4179.     {
  4180.       caracter_temporal = cadena[i];
  4181.       cadena[i] = cadena[j];
  4182.       cadena[j] = caracter_temporal;
  4183.     }
  4184. }
  4185.  
  4186. /*
  4187.   Invierte el contenido de cadena.
  4188.  
  4189.   Si ocurre un error en el proceso, esta función devuelve -1. En caso
  4190.   contrario devuelve 0.
  4191.  
  4192.   Este método requiere (2.0 * n) intercambios, siendo n el número de
  4193.   elementos del array.
  4194.  
  4195.   Dado una cadena de 512 caracteres, el primer método (función anterior)
  4196.   requiere 768 intercambios, mientras que el segundo método (esta función)
  4197.   requiere 1024 intercambios.
  4198. */
  4199.  
  4200. int invertir_cadena (char cadena[])
  4201. {
  4202.   char *cadena_temporal;
  4203.  
  4204.   register int i, j;
  4205.  
  4206.   for (j = 0; cadena[j]; ++j) /* encuentra el final de cadena */
  4207.     ;
  4208.  
  4209.   if ((cadena_temporal = (char *) calloc (1, j)) == '\0')
  4210.     return (-1); /* no se pudo asignar memoria */
  4211.  
  4212.   for (i = 0, j--; j >= 0; i++, j--)
  4213.     cadena_temporal[i] = cadena[j];
  4214.  
  4215.   for (j = 0; j < i; j++)
  4216.     cadena[j] = cadena_temporal[j];
  4217.  
  4218.   free (cadena_temporal);
  4219.  
  4220.   return (0);
  4221. }
  4222.  
  4223. /*
  4224.   Intercambia el contenido de dos cadenas.
  4225. */
  4226.  
  4227. void intercambiar_cadenas_rapido (char cadena1[], char cadena2[])
  4228. {
  4229.   register int i, j;
  4230.   char caracter_temporal;
  4231.  
  4232.   for (i = 0; cadena1[i] && cadena2[j]; i++)
  4233.     {
  4234.       caracter_temporal = cadena1[i];
  4235.       cadena1[i] = cadena2[i];
  4236.       cadena2[i] = caracter_temporal;
  4237.     }
  4238.  
  4239.   if (cadena1[i])
  4240.     {
  4241.       j = i;
  4242.       while (cadena1[i])
  4243.         cadena2[i] = cadena1[i++];
  4244.       cadena2[i] = '\0';
  4245.       cadena1[j] = '\0';
  4246.     }
  4247.   else
  4248.     {
  4249.       j = i;
  4250.       while (cadena2[i])
  4251.         cadena1[i] = cadena2[i++];
  4252.       cadena1[i] = '\0';
  4253.       cadena2[j] = '\0';
  4254.     }
  4255. }
  4256.  
  4257. /*
  4258.   Intercambia el contenido de dos cadenas.
  4259.   Los parámetros tamanio1 y tamanio2 representan los números máximos de
  4260.   caracteres en cadena1 y cadena2 respectivamente.
  4261.  
  4262.   Devuelve uno de los siguientes valores:
  4263.      0: intercambio con éxito
  4264.     -1: memoria insuficiente
  4265.      1: intercambio incompleto
  4266. */
  4267.  
  4268. int intercambiar_cadenas (char cadena1[], char cadena2[],
  4269.                           int tamanio1, int tamanio2)
  4270. {
  4271.   register int i, j;
  4272.   char caracter_temporal;
  4273.  
  4274.   for (i = 0; cadena1[i]; i++) /* obtiene la longitud de cadena1 */
  4275.     ;
  4276.  
  4277.   if (i >= tamanio2) /* ¿demasiado grande para cadena2? */
  4278.     return (1);
  4279.  
  4280.   for (i = 0; cadena2[i]; i++) /* obtiene la longitud de cadena2 */
  4281.     ;
  4282.  
  4283.   if (i >= tamanio1) /* ¿demasiado grande para cadena1? */
  4284.     return (1);
  4285.  
  4286.   for (i = 0; cadena1[i] && cadena2[i]; i++)
  4287.     {
  4288.       caracter_temporal = cadena1[i];
  4289.       cadena1[i] = cadena2[i];
  4290.       cadena2[i] = caracter_temporal;
  4291.     }
  4292.  
  4293.   if (cadena1[i])
  4294.     {
  4295.       j = i;
  4296.       while (cadena1[i])
  4297.         cadena2[i] = cadena1[i++];
  4298.       cadena2[i] = '\0';
  4299.       cadena1[j] = '\0';
  4300.     }
  4301.   else if (cadena2[i])
  4302.     {
  4303.       j = i;
  4304.       while (cadena2[i])
  4305.         cadena1[i] = cadena2[i++];
  4306.       cadena1[i] = '\0';
  4307.       cadena2[j] = '\0';
  4308.     }
  4309.  
  4310.   return (0);
  4311. }
  4312.  
  4313. /*
  4314.   Coloca el número de blancos especificado al comienzo de cadena.
  4315.  
  4316.   Devuelve el valor -1 si no existe memoria suficiente.
  4317. */
  4318.  
  4319. int rellenar_cadena_rapido (char cadena[], int numero_de_blancos)
  4320. {
  4321.   register int i, j;
  4322.  
  4323.   char *cadena_temporal;
  4324.  
  4325.   for (i = 0; cadena[i]; i++) /* obtiene la longitud de cadena */
  4326.     ;
  4327.  
  4328.   if ((cadena_temporal = (char *) calloc (1, i + numero_de_blancos + 1)) == '\0')
  4329.     return (-1); /* no se puedo obtener memoria */
  4330.  
  4331.   for (i = 0; i < numero_de_blancos; i++)
  4332.     cadena_temporal[i] = ' ';
  4333.  
  4334.   for (j = 0; cadena_temporal[i] = cadena[j]; ++j, ++i)
  4335.     ;
  4336.  
  4337.   cadena_temporal[i] = '\0';
  4338.  
  4339.   for (i = 0; cadena[i] = cadena_temporal[i]; i++)
  4340.     ;
  4341.  
  4342.   free (cadena_temporal);
  4343.  
  4344.   return (0);
  4345. }
  4346.  
  4347. /*
  4348.   Coloca el número de blancos especificado al comienzo de cadena.
  4349.  
  4350.   Devuelve uno de los siguientes valores:
  4351.     -1: Memoria suficiente.
  4352.      0: Exito.
  4353.      1: Incompleto.
  4354. */
  4355.  
  4356. int rellenar_cadena (char cadena[], int numero_de_blancos,
  4357.                      int numero_maximo_de_caracteres)
  4358. {
  4359.   register int i, j;
  4360.  
  4361.   char *cadena_temporal;
  4362.  
  4363.   for (i = 0; cadena[i]; i++) /* obtiene la longitud de cadena */
  4364.     ;
  4365.  
  4366.   if (i + numero_de_blancos >= numero_maximo_de_caracteres)
  4367.     return (1);
  4368.   else if ((cadena_temporal = (char *) calloc (1, i + numero_de_blancos + 1)) == '\0')
  4369.     return (-1); /* no se puedo obtener memoria */
  4370.  
  4371.   for (i = 0; i < numero_de_blancos; i++)
  4372.     cadena_temporal[i] = ' ';
  4373.  
  4374.   for (j = 0; cadena_temporal[i] = cadena[j]; ++j, ++i)
  4375.     ;
  4376.  
  4377.   cadena_temporal[i] = '\0';
  4378.  
  4379.   for (i = 0; cadena[i] = cadena_temporal[i]; i++)
  4380.     ;
  4381.  
  4382.   free (cadena_temporal);
  4383.  
  4384.   return (0);
  4385. }
  4386.  
  4387. /*
  4388.   Devuelve el número de ocurrencias del carácter especificado en cadena.
  4389. */
  4390.  
  4391. int contar_caracter_en_cadena (char cadena[], char caracter)
  4392. {
  4393.   register int i, contador = 0;
  4394.  
  4395.   for (i = 0; cadena[i]; i++)
  4396.     if (cadena[i] == caracter)
  4397.       contador++;
  4398.  
  4399.   return (contador);
  4400. }
  4401.  
  4402. /*
  4403.   Quita todas las ocurrencias del carácter especificado en cadena.
  4404.  
  4405.   Devuelve el valor -1 si no hubo suficiente memoria para la operación
  4406.   de borrado, y 0 en otro caso.
  4407. */
  4408.  
  4409. int quitar_caracter_de_cadena (char cadena[], char caracter)
  4410. {
  4411.   register int i, j;
  4412.  
  4413.   char *cadena_temporal;
  4414.  
  4415.   for (i = 0; cadena[i]; i++)
  4416.     ;
  4417.  
  4418.   if ((cadena_temporal = (char *) calloc (1, i)) == '\0')
  4419.     return (-1);
  4420.  
  4421.   for (i = 0, j = 0; cadena[i]; i++)
  4422.     if (cadena[i] != caracter)
  4423.       cadena_temporal[j++] = cadena[i];
  4424.  
  4425.   for (cadena_temporal[j] = '\0', i = 0; cadena[i] = cadena_temporal[i]; i++)
  4426.     ;
  4427.  
  4428.   free (cadena_temporal);
  4429.  
  4430.   return (0);
  4431. }
  4432.  
  4433. /*
  4434.   Devuelve el índice de cadena que contiene la primera ocurrencia del
  4435.   carácter especificado. Si el carácter no se encuentra, devuelve -1.
  4436. */
  4437.  
  4438. int indice_de_caracter_en_cadena (char cadena[], char caracter)
  4439. {
  4440.   register int i, indice_caracter = -1;
  4441.  
  4442.   for (i = 0; cadena[i] && indice_caracter == -1; i++)
  4443.     if (cadena[i] == caracter)
  4444.       indice_caracter = i;
  4445.  
  4446.   return (indice_caracter); /* -1 si no se encontró */
  4447. }
  4448.  
  4449. /*
  4450.   Devuelve el índice que contiene la primera ocurrencia del carácter
  4451.   especificado más a la derecha en cadena. Si el carácter no se encuentra,
  4452.   devuelve -1.
  4453. */
  4454.  
  4455. int indice_de_caracter_mas_a_la_derecha_en_cadena (char cadena[], char caracter)
  4456. {
  4457.   register int i, indice_caracter = -1;
  4458.  
  4459.   for (i = 0; cadena[i]; i++)
  4460.     if (cadena[i] == caracter)
  4461.       indice_caracter = i;
  4462.  
  4463.   return (indice_caracter); /* -1 si no se encontró */
  4464. }
  4465.  
  4466. /*
  4467.   Reemplaza todas las ocurrencias de caracter_fuente con caracter_destino
  4468.   dentro de cadena.
  4469. */
  4470.  
  4471. void reemplazar_caracter_en_cadena (char cadena[], char caracter_fuente,
  4472.                                     char caracter_destino)
  4473. {
  4474.   register int i;
  4475.  
  4476.   if (caracter_fuente != caracter_destino)
  4477.     for (i = 0; cadena[i]; i++)
  4478.       if (cadena[i] == caracter_fuente)
  4479.         cadena[i] = caracter_destino;
  4480. }
  4481.  
  4482. /*
  4483.   Coloca un número especificado de ocurrencias de un caracter dado en cadena.
  4484.   Devuelve 1 si el llenado no tuvo éxito o 0 en caso contrario.
  4485. */
  4486.  
  4487. int llenar_cadena (char cadena[], int caracter, int contador,
  4488.                    int numero_maximo_de_caracteres)
  4489. {
  4490.   register int i;
  4491.  
  4492.   if (contador + 1 > numero_maximo_de_caracteres) /* +1 reserva espacio para nulo */
  4493.     return (1); /* memoria insuficiente */
  4494.  
  4495.   for (i = 0; i < contador; ++i) /* llena la cadena */
  4496.     cadena[i] = caracter;
  4497.  
  4498.   cadena[i] = '\0';
  4499.  
  4500.   return (0);
  4501. }
  4502.  
  4503. /*
  4504.   Devuelve el índice del primer carácter que no es espacio blanco (un blanco
  4505.   o un tabulador) en cadena. Si todos los caracteres en la cadena son blanco,
  4506.   entonces devuelve -1.
  4507. */
  4508.  
  4509. int primer_caracter_no_blanco_en_cadena (char cadena[])
  4510. {
  4511.   register int i, indice_primer_caracter_no_blanco = -1;
  4512.  
  4513.   for (i = 0; cadena[i] && indice_primer_caracter_no_blanco == -1; ++i)
  4514.     if (cadena[i] != ' ' && cadena[i] != '\t')
  4515.       indice_primer_caracter_no_blanco = i;
  4516.  
  4517.   return (indice_primer_caracter_no_blanco); /* -1 si todos los espacios son blancos */
  4518. }
  4519.  
  4520. /*
  4521.   Devuelve el índice del último carácter que no es espacio blanco (un blanco
  4522.   o un tabulador) en cadena. Si todos los caracteres en la cadena son blanco,
  4523.   entonces devuelve -1.
  4524. */
  4525.  
  4526. int ultimo_caracter_no_blanco_en_cadena (char cadena[])
  4527. {
  4528.   register int i, indice_ultimo_caracter_no_blanco = -1;
  4529.  
  4530.   for (i = 0; cadena[i]; ++i)
  4531.     if (cadena[i] != ' ' && cadena[i] != '\t')
  4532.       indice_ultimo_caracter_no_blanco = i;
  4533.  
  4534.   return (indice_ultimo_caracter_no_blanco); /* -1 si todos los espacios son blancos */
  4535. }
  4536.  
  4537. /*
  4538.   Devuelve 1 si las cadenas cadena1 y cadena2 son iguales, en cualquier otro
  4539.   caso devuelve 0. Soporta proceso sensitivo al caso.
  4540. */
  4541.  
  4542. int cadenas_iguales (char cadena1[], char cadena2[], int ignorar_caso)
  4543. {
  4544.   register int i;
  4545.   char caracter1, caracter2;
  4546.  
  4547.   for (i = 0; cadena1[i] && cadena2[i]; i++)
  4548.     if (cadena1[i] != cadena2[i])
  4549.       if (ignorar_caso)
  4550.         {
  4551.           caracter1 = cadena1[i] >= 'a' && cadena1[i] <= 'z' ?
  4552.                       cadena1[i] & ~32 : cadena1[i];
  4553.           caracter2 = cadena2[i] >= 'a' && cadena2[i] <= 'z' ?
  4554.                       cadena2[i] & ~32 : cadena2[i];
  4555.           if (caracter1 != caracter2)
  4556.             break;
  4557.         }
  4558.       else
  4559.         break;
  4560.  
  4561.   if (cadena1[i] || cadena2[i])
  4562.     return (0);
  4563.   else
  4564.     return (1);
  4565. }
  4566.  
  4567. /*
  4568.   Devuelve el índice de la primera diferencia entre dos cadenas o el valor -1
  4569.   si las dos cadenas son iguales.
  4570. */
  4571.  
  4572. int primera_diferencia_de_cadenas (char cadena1[], char cadena2[],
  4573.                                    int ignorar_caso)
  4574. {
  4575.   register int i;
  4576.   char caracter1, caracter2;
  4577.  
  4578.   for (i = 0; cadena1[i] && cadena2[i]; i++)
  4579.     if (cadena1[i] != cadena2[i])
  4580.       if (ignorar_caso)
  4581.         {
  4582.           caracter1 = cadena1[i] >= 'a' && cadena1[i] <= 'z' ?
  4583.                       cadena1[i] & ~32 : cadena1[i];
  4584.           caracter2 = cadena2[i] >= 'a' && cadena2[i] <= 'z' ?
  4585.                       cadena2[i] & ~32 : cadena2[i];
  4586.           if (caracter1 != caracter2)
  4587.             break;
  4588.         }
  4589.       else
  4590.         break;
  4591.  
  4592.   if (cadena1[i] || cadena2[i])
  4593.     return (i);
  4594.   else
  4595.     return (-1);
  4596. }
  4597.  
  4598. /*
  4599.   Compara las cadenas especificadas. Devuelve 1 si cadena1 > cadena2,
  4600.   2 si cadena2 > cadena1 y 0 si las cadenas son iguales. Soporta proceso
  4601.   sensitivo al caso.
  4602. */
  4603.  
  4604. int comparar_cadenas (char cadena1[], char cadena2[], int ignorar_caso)
  4605. {
  4606.   register int i;
  4607.   char caracter1, caracter2;
  4608.   int resultado = 0; /* 0 igual, 1 cadena1 mayor, 2 cadena2 mayor */
  4609.  
  4610.   for (i = 0; cadena1[i] && cadena2[i]; i++)
  4611.     if (cadena1[i] != cadena2[i])
  4612.       {
  4613.         if (ignorar_caso)
  4614.           {
  4615.             caracter1 = cadena1[i] >= 'a' && cadena1[i] <= 'z' ?
  4616.                         cadena1[i] & ~32 : cadena1[i];
  4617.             caracter2 = cadena2[i] >= 'a' && cadena2[i] <= 'z' ?
  4618.                         cadena2[i] & ~32 : cadena2[i];
  4619.             if (caracter1 != caracter2)
  4620.               {
  4621.                 if (caracter1 > caracter2)
  4622.                   resultado = 1;
  4623.                 else
  4624.                   resultado = 2;
  4625.                 break;
  4626.               }
  4627.           }
  4628.         else
  4629.           {
  4630.             if (cadena1[i] > cadena2[i])
  4631.               resultado = 1;
  4632.             else
  4633.               resultado = 2;
  4634.             break;
  4635.           }
  4636.       }
  4637.  
  4638.   if (resultado == 0)
  4639.     {
  4640.       if (cadena1[i] == cadena2[i])
  4641.         resultado = 0;
  4642.       else if (cadena1[i])
  4643.         resultado = 1;
  4644.       else
  4645.         resultado = 2;
  4646.     }
  4647.  
  4648.   return (resultado);
  4649. }
  4650.  
  4651. /*
  4652.   Devuelve el índice de comienzo de subcadena dentro de cadena o el valor -1
  4653.   si subcadena no se encuentra en cadena.
  4654. */
  4655.  
  4656. int indice_subcadena_en_cadena (char subcadena[], char cadena[])
  4657. {
  4658.   register int i, j, k;
  4659.  
  4660.   for (i = 0; cadena[i]; i++)
  4661.     for (j = i, k = 0; cadena[j] == subcadena[k]; j++, k++)
  4662.       if (! subcadena[k+1]) /* fin de subcadena */
  4663.         return (i);
  4664.  
  4665.   return (-1); /* subcadena no encontrada */
  4666. }
  4667.  
  4668. /*
  4669.   Devuelve el número de ocurrencias de subcadena dentro de cadena.
  4670. */
  4671.  
  4672. int contador_de_subcadenas_en_cadena (char subcadena[], char cadena[])
  4673. {
  4674.   register int i, j, k;
  4675.   int contador = 0;
  4676.  
  4677.   for (i = 0; cadena[i]; i++)
  4678.     for (j = i, k = 0; cadena[j] == subcadena[k]; j++, k++)
  4679.       if (! subcadena[k+1]) /* fin de subcadena */
  4680.         contador++;
  4681.  
  4682.   return (contador); /* 0 si subcadena no encontrada */
  4683. }
  4684.  
  4685. /*
  4686.   Quita la primera ocurrencia de subcadena dentro de cadena.
  4687.   Si tiene éxito, devuelve el valor 0. Si subcadena no es encontrada,
  4688.   devuelve el valor -1.
  4689. */
  4690.  
  4691. int quitar_subcadena_de_cadena (char subcadena[], char cadena[])
  4692. {
  4693.   register int i, j, k;
  4694.   int indice_subcadena_en_cadena = -1;
  4695.  
  4696.   for (i = 0; cadena[i] && indice_subcadena_en_cadena == -1; i++)
  4697.     for (j = i, k = 0; cadena[j] == subcadena[k]; j++, k++)
  4698.       if (! subcadena[k+1]) /* fin de subcadena */
  4699.         indice_subcadena_en_cadena = i;
  4700.  
  4701.   if (indice_subcadena_en_cadena != -1)
  4702.     {
  4703.       for (k = 0; subcadena[k]; k++)
  4704.         ;
  4705.  
  4706.       for (j = indice_subcadena_en_cadena, i = indice_subcadena_en_cadena + k;
  4707.            cadena[i]; j++, i++)
  4708.         cadena[j] = cadena[i];
  4709.  
  4710.       cadena[j] = '\0';
  4711.  
  4712.       return (0);
  4713.     }
  4714.   else
  4715.     return (-1); /* subcadena no encontrada */
  4716. }
  4717.  
  4718. /*
  4719.   Devuelve el índice a la próxima ocurrencia de subcadena dentro de cadena
  4720.   comenzando en el índice especificado. Si subcadena no es encontrada, -1
  4721.   es devuelto.
  4722. */
  4723.  
  4724. int indice_proxima_ocurrencia_de_subcadena_en_cadena (char subcadena[],
  4725.                                                       char cadena[], int indice)
  4726. {
  4727.   register i, j, k;
  4728.  
  4729.   for (i = indice; cadena[i]; i++)
  4730.     for (j = i, k = 0; cadena[j] == subcadena[k]; j++, k++)
  4731.       if (! subcadena[k+1]) /* fin de subcadena */
  4732.         return (i);
  4733.  
  4734.   return (-1); /* subcadena no encontrada */
  4735. }
  4736.  
  4737. /*
  4738.   Devuelve el índice de comienzo de subcadena dentr de cadena. Si subcadena
  4739.   no es encontrada, devuelve -1.
  4740.  
  4741.   El array subcadena se puede considerar como un patrón, puesto que esta
  4742.   función permite que subcadena contenga el carácter comodín ? que es
  4743.   equivalente a cualquier carácter en las comparaciones.
  4744. */
  4745.  
  4746. int indice_patron_en_cadena (char subcadena[], char cadena[])
  4747. {
  4748.   register int i, j, k;
  4749.  
  4750.   for (i = 0; cadena[i]; i++)
  4751.     for (j = i, k = 0; cadena[j] == subcadena[k] || subcadena[k] == '?'; j++, k++)
  4752.       if (! subcadena[k+1]) /* fin de subcadena */
  4753.         return (i);
  4754.  
  4755.   return (-1);
  4756. }
  4757. ende
  4758. begine " VUELTA DEL CABALLO "
  4759. /*
  4760.   Este programa realiza lo siguiente: Se da un tablero de nxn con n*n
  4761.   cuadros. Un caballo -que puede moverse según las reglas del ajedrez-
  4762.   se sitúa en el cuadro de coordenadas (x0,y0). Se pide encontrar, si
  4763.   existe, un recubrimiento del tablero completo, o sea, calcular un
  4764.   circuito de n*n-1 movimientos de forma que cada cuadro del tablero
  4765.   sea visitado exactamente una vez.
  4766.  
  4767.   La solución a este problema está basado en el método de tanteo
  4768.   sistemático (intento y error).
  4769.  
  4770.   La función más importante es ensayar() cuyo pseudocódigo es el siguiente:
  4771.  
  4772.     <PRINCIPIO ensayar>
  4773.       REPETIR seleccionar el nuevo candidato de la lista de futuros movimientos
  4774.         SI aceptable ENTONCES
  4775.           SI tablero no lleno ENTONCES
  4776.             LLAMAR ensayar nuevo movimiento
  4777.             SI no acertado ENTONCES
  4778.               borrar la anotación anterior
  4779.             FINSI
  4780.           FINSI
  4781.         FINSI
  4782.       HASTA movimiento acertado O no hay más posibilidades
  4783.     <FIN>
  4784.  
  4785.   Observaciones sobre el código:
  4786.  
  4787.     1) Estudiar la función ensayar() a partir de este pseudocódigo.
  4788.  
  4789.     2) El método utilizado para obtener el movimiento del caballo de (x1,y1)
  4790.        hasta (x2,y2) es sumar a (x1,y1) los vectores de diferencias.
  4791.  
  4792.        Los vectores de diferencia dif_x y dif_y contienen la diferencia de
  4793.        la coordenada x e y respectivamente desde la posición actual del
  4794.        caballo.
  4795.  
  4796.        Veáse con el siguiente tablero:
  4797.  
  4798.            0  6  0  7  0
  4799.  
  4800.            5  0  0  0  8
  4801.  
  4802.            0  0  C  0  0
  4803.  
  4804.            4  0  0  0  1
  4805.  
  4806.            0  3  0  2  0
  4807.  
  4808.        C representa la posición del caballo; los números del 1 al 8 respre-
  4809.        sentan los 8 posibles movimientos. El primer movimiento se obtiene:
  4810.          x2 = x1 + 2; y2 = y1 + 1;
  4811.  
  4812.     3) La macro tab() se utiliza para trabajar con los índices de 1 a n
  4813.        en la matriz del tablero en vez de con los índices reales 0 a n-1.
  4814.  
  4815.     4) La condición «tablero no lleno» se expresa mediante «i < n*n» donde
  4816.        i es el número de movimiento del caballo actual y n la dimensión del
  4817.        tablero.
  4818.  
  4819.     5) El significado de las asignaciones a los elementos de la matriz es:
  4820.  
  4821.       tab (x, y) = 0; /* el cuadro <x,y> no ha sido visitado */
  4822.       tab (x, y) = i; /* el cuador <x,y> ha sido visitado en el movimiento
  4823.                          i-ésimo (1 ≤ i ≤ n*n) */
  4824.  
  4825.   NOTA: Con un dimensión de la matriz superior a 4, el proceso de encontrar
  4826.   la solución es muy lento. Por eso se ha puesto el límite en 8 aunque ya
  4827.   con este número el proceso es superlento (en términos de media, ya que
  4828.   puede dar la casualidad de que se encuentre la solución en los primeros
  4829.   intentos).
  4830. */
  4831.  
  4832. /* Ficheros a incluir: */
  4833.  
  4834. #include <stdio.h>  /* printf () */
  4835. #include <conio.h>  /* getch () */
  4836. #include <stdlib.h> /* exit () */
  4837.  
  4838. /* Macros: */
  4839.  
  4840. #define NUM_MOVIMIENTOS_POSIBLES 8
  4841. #define NMAXIMO 8
  4842.  
  4843. #define BOOLEAN int
  4844. #define TRUE    1
  4845. #define FALSE   0
  4846.  
  4847. #define ESC 27
  4848.  
  4849. #define en(x,x1,x2) ((x) >= (x1) && (x) <= (x2))
  4850.  
  4851. #define tab(i,j) tablero[(i)-1][(j)-1] /* tab(1,1) es en realidad tablero[0][0] */
  4852.  
  4853. /* Variables globales: */
  4854.  
  4855. int n, tablero[NMAXIMO][NMAXIMO];
  4856. BOOLEAN movimiento_acertado;
  4857. int dif_x [NUM_MOVIMIENTOS_POSIBLES] = { 2, 1, -1, -2, -2, -1, 1, 2 },
  4858.     dif_y [NUM_MOVIMIENTOS_POSIBLES] = { 1, 2, 2, 1, -1, -2, -2, -1 };
  4859.  
  4860. /* Prototipos de las funciones: */
  4861.  
  4862. void proceso (void);
  4863. void ensayar (int i, int x, int y);
  4864.  
  4865. /* Definiciones de las funciones: */
  4866.  
  4867. void main (void)
  4868. {
  4869.   do
  4870.     {
  4871.       printf ("\n\nVUELTA DEL CABALLO:\n  ");
  4872.       proceso ();
  4873.       printf ("\nPulsa cualquier tecla para repetir o ESC para salir. ");
  4874.     } while (getch () != ESC);
  4875. }
  4876.  
  4877. void proceso (void)
  4878. {
  4879.   register int i, j;
  4880.   int x0, y0;
  4881.  
  4882.   for (i = 1; i <= n; i++)
  4883.     for (j = 1; j <= n; j++)
  4884.       tab (i, j) = 0;
  4885.  
  4886.   printf ("\nIntroduce dimensión del tablero (1 ≤ n ≤ %d, n > 4 es muy lento): ", NMAXIMO);
  4887.   do
  4888.     {
  4889.       n = getch () - '0';
  4890.     } while (! en (n, 1, NMAXIMO));
  4891.   putch (n + '0');
  4892.  
  4893.   printf ("\nFila inicial (1 ≤ x ≤ %d): ", n);
  4894.   do
  4895.     {
  4896.       x0 = getch () - '0';
  4897.     } while (! en (x0, 1, n));
  4898.   putch (x0 + '0');
  4899.  
  4900.   printf ("\nColumna inicial (1 ≤ y ≤ %d): ", n);
  4901.   do
  4902.     {
  4903.       y0 = getch () - '0';
  4904.     } while (! en (y0, 1, n));
  4905.   putch (y0 + '0');
  4906.  
  4907.   tab (x0, y0) = 1;
  4908.   printf ("\n\n");
  4909.   ensayar (2, x0, y0);
  4910.  
  4911.   if (movimiento_acertado)
  4912.     for (printf ("\n\nLA SOLUCION ES:\n  "), i = 1; i <= n; i++)
  4913.       {
  4914.         for (j = 1; j <= n; j++)
  4915.           printf ("%2d ", tab (i, j));
  4916.         printf ("\n  ");
  4917.       }
  4918.   else
  4919.     printf ("\n\nNO HAY SOLUCION.\n");
  4920. }
  4921.  
  4922. void ensayar (int i, int x1, int y1)
  4923. {
  4924.   int movimientos_realizados = 0;
  4925.   int x2, y2;
  4926.   const ncuadrado = n * n;
  4927.   static long unsigned num_movimientos_caballo = 0;
  4928.  
  4929.   do
  4930.     {
  4931.       movimiento_acertado = FALSE;
  4932.       x2 = x1 + dif_x[movimientos_realizados];
  4933.       y2 = y1 + dif_y[movimientos_realizados];
  4934.       movimientos_realizados++;
  4935.       if (kbhit ())
  4936.         if (getch () == ESC)
  4937.           exit (1);
  4938.       printf ("Número de movimientos del caballo (ESC para salir): %ld\r", ++num_movimientos_caballo);
  4939.       if (en (x2, 1, n) && en (y2, 1, n) && tab (x2, y2) == 0)
  4940.         {
  4941.           tab (x2, y2) = i;
  4942.           if (i < ncuadrado)
  4943.             {
  4944.               ensayar (i+1, x2, y2);
  4945.               if (! movimiento_acertado)
  4946.                 tab (x2, y2) = 0;
  4947.             }
  4948.           else
  4949.             movimiento_acertado = TRUE;
  4950.         }
  4951.     } while (! movimiento_acertado &&
  4952.              movimientos_realizados != NUM_MOVIMIENTOS_POSIBLES);
  4953. }
  4954. ende
  4955. begine " RELLENO DE UN TABLERO SEGUN UNAS REGLAS DADAS "
  4956. /*
  4957.   Este programa es una variante del anterior (la vuelta del caballo). La
  4958.   idea es la misma, lo único que cambia es la forma de los movimientos:
  4959.   desde un determinado punto nos podemos mover tres cuadros (dejando dos
  4960.   en medio) en horizontal y vertical y dos cuadros (dejando uno en medio)
  4961.   en diagonal.
  4962.  
  4963.   Ejemplo:
  4964.  
  4965.      1  9 12  4 17
  4966.     20 23 15  7 22
  4967.     11  5 18 10 13
  4968.      2  8 21  3 16
  4969.     19 24 14  6 25
  4970.  
  4971.   La única diferencia entre este programa y el anterior está en algunos
  4972.   nombres de mensajes y variables, y también en el contenido de los vectores
  4973.   dif_x y dif_y.
  4974. */
  4975.  
  4976. /* Ficheros a incluir: */
  4977.  
  4978. #include <stdio.h>  /* printf () */
  4979. #include <conio.h>  /* getch () */
  4980. #include <stdlib.h> /* exit () */
  4981.  
  4982. /* Macros: */
  4983.  
  4984. #define NUM_MOVIMIENTOS_POSIBLES 8
  4985. #define NMAXIMO 8
  4986.  
  4987. #define BOOLEAN int
  4988. #define TRUE    1
  4989. #define FALSE   0
  4990.  
  4991. #define ESC 27
  4992.  
  4993. #define en(x,x1,x2) ((x) >= (x1) && (x) <= (x2))
  4994.  
  4995. #define tab(i,j) tablero[(i)-1][(j)-1] /* tab(1,1) es en realidad tablero[0][0] */
  4996.  
  4997. /* Variables globales: */
  4998.  
  4999. int n, tablero[NMAXIMO][NMAXIMO];
  5000. BOOLEAN movimiento_acertado;
  5001. int dif_x [NUM_MOVIMIENTOS_POSIBLES] = { -3, -2, 0, 2, 3, 2, 0, -2 },
  5002.     dif_y [NUM_MOVIMIENTOS_POSIBLES] = {  0, -2, -3, -2, 0, 2, 3, 2 };
  5003.  
  5004. /* Prototipos de las funciones: */
  5005.  
  5006. void proceso (void);
  5007. void ensayar (int i, int x, int y);
  5008.  
  5009. /* Definiciones de las funciones: */
  5010.  
  5011. void main (void)
  5012. {
  5013.   do
  5014.     {
  5015.       printf ("\n\nRELLENO DE UN TABLERO SEGUN UNAS REGLAS DADAS:\n  ");
  5016.       proceso ();
  5017.       printf ("\nPulsa cualquier tecla para repetir o ESC para salir. ");
  5018.     } while (getch () != ESC);
  5019. }
  5020.  
  5021. void proceso (void)
  5022. {
  5023.   register int i, j;
  5024.   int x0, y0;
  5025.  
  5026.   for (i = 1; i <= n; i++)
  5027.     for (j = 1; j <= n; j++)
  5028.       tab (i, j) = 0;
  5029.  
  5030.   printf ("\nIntroduce dimensión del tablero (1 ≤ n ≤ %d, n > 4 es muy lento): ", NMAXIMO);
  5031.   do
  5032.     {
  5033.       n = getch () - '0';
  5034.     } while (! en (n, 1, NMAXIMO));
  5035.   putch (n + '0');
  5036.  
  5037.   printf ("\nFila inicial (1 ≤ x ≤ %d): ", n);
  5038.   do
  5039.     {
  5040.       x0 = getch () - '0';
  5041.     } while (! en (x0, 1, n));
  5042.   putch (x0 + '0');
  5043.  
  5044.   printf ("\nColumna inicial (1 ≤ y ≤ %d): ", n);
  5045.   do
  5046.     {
  5047.       y0 = getch () - '0';
  5048.     } while (! en (y0, 1, n));
  5049.   putch (y0 + '0');
  5050.  
  5051.   tab (x0, y0) = 1;
  5052.   printf ("\n\n");
  5053.   ensayar (2, x0, y0);
  5054.  
  5055.   if (movimiento_acertado)
  5056.     for (printf ("\n\nLA SOLUCION ES:\n  "), i = 1; i <= n; i++)
  5057.       {
  5058.         for (j = 1; j <= n; j++)
  5059.           printf ("%2d ", tab (i, j));
  5060.         printf ("\n  ");
  5061.       }
  5062.   else
  5063.     printf ("\n\nNO HAY SOLUCION.\n");
  5064. }
  5065.  
  5066. void ensayar (int i, int x1, int y1)
  5067. {
  5068.   int movimientos_realizados = 0;
  5069.   int x2, y2;
  5070.   const ncuadrado = n * n;
  5071.   static long unsigned num_movimientos = 0;
  5072.  
  5073.   do
  5074.     {
  5075.       movimiento_acertado = FALSE;
  5076.       x2 = x1 + dif_x[movimientos_realizados];
  5077.       y2 = y1 + dif_y[movimientos_realizados];
  5078.       movimientos_realizados++;
  5079.       if (kbhit ())
  5080.         if (getch () == ESC)
  5081.           exit (1);
  5082.       printf ("Número de movimientos (ESC para salir): %ld\r", ++num_movimientos);
  5083.       if (en (x2, 1, n) && en (y2, 1, n) && tab (x2, y2) == 0)
  5084.         {
  5085.           tab (x2, y2) = i;
  5086.           if (i < ncuadrado)
  5087.             {
  5088.               ensayar (i+1, x2, y2);
  5089.               if (! movimiento_acertado)
  5090.                 tab (x2, y2) = 0;
  5091.             }
  5092.           else
  5093.             movimiento_acertado = TRUE;
  5094.         }
  5095.     } while (! movimiento_acertado &&
  5096.              movimientos_realizados != NUM_MOVIMIENTOS_POSIBLES);
  5097. }
  5098. ende
  5099. begine " PROBLEMA DE LAS OCHO REINAS "
  5100. /*
  5101.   El problema de las ocho reinas consiste en situar ocho reinas en un
  5102.   tablero de ajedrez, de forma que ninguna reina pueda actuar sobre
  5103.   cualquiera de las otras.
  5104.  
  5105.   El programa es similar a los dos anteriores, el pseudocódigo de la
  5106.   función ensayar() es el siguiente:
  5107.  
  5108.     <PRINCIPIO ensayar> (i: entero)
  5109.       inicializar el conjunto de posiciones de la reina i-ésima
  5110.       REPETIR hacer la selección siguiente
  5111.         SI segura ENTONCES
  5112.           poner reina
  5113.           SI i < 8 ENTONCES
  5114.             LLAMAR ensayar (i + 1)
  5115.             SI no acertado ENTONCES
  5116.               quitar reina
  5117.             FINSI
  5118.           FINSI
  5119.         FINSI
  5120.       HASTA acertada O no hay más posiciones
  5121.     <FIN>
  5122.  
  5123.   Observaciones sobre el código:
  5124.  
  5125.     1) Estudiar la función ensayar() a partir de este pseudocódigo.
  5126.  
  5127.     2) Vectores utilizados:
  5128.  
  5129.        int posiciones_en_columna[8];          /* RANGO: 1..8  */
  5130.        BOOLEAN reina_en_fila[8];              /* RANGO: 1..8  */
  5131.        BOOLEAN reina_en_diagonal_normal[15];  /* RANGO: -7..7 */
  5132.        BOOLEAN reina_en_diagonal_inversa[15]; /* RANGO: 2..16 */
  5133.  
  5134.        En C, el primer elemento de cada vector tiene índice 0, esto es
  5135.        fácil solucionarlo con las siguientes macros:
  5136.  
  5137.        #define c(i) posiciones_en_columna[(i)-1]
  5138.        #define f(i) reina_en_fila[(i)-1]
  5139.        #define dn(i) reina_en_diagonal_normal[(i)+7]
  5140.        #define di(i) reina_en_diagonal_inversa[(i)-2]
  5141.  
  5142.        Significado de los vectores:
  5143.  
  5144.        c(i) : la posición de la reina en la columna i
  5145.        f(j) : indicativo de que no hay reina en la fila j-ésima
  5146.        dn(k): indicativo de que no hay reina en la diagonal normal (\) k-ésima
  5147.        di(k): indicativo de que no hay reina en la diagonal invertida (/) k-ésima
  5148.  
  5149.        Dado que se sabe, por las reglas del ajedrez, que una reina actúa
  5150.        sobre todas las piezas situadas en la misma columna, fila o diagonal
  5151.        del tablero se deduce que cada columna puede contener una y sólo una
  5152.        reina, y que la elección de la situación de la reina i-ésima puede
  5153.        restringirse a los cuadros de la columna i. Por tanto, el parámetro
  5154.        i se convierte en el índice de columna, y por ello el proceso de
  5155.        selección de posiciones queda limitado a los ocho posibles valores
  5156.        del índice de fila j.
  5157.  
  5158.        A partir de estos datos, la línea poner reina del pseudocódigo es:
  5159.  
  5160.          c (i) = j; f (j) = di (i + j) = dn (i - j) = FALSE;
  5161.  
  5162.        y la línea quitar reina del pseudocódigo:
  5163.  
  5164.          f (j) = di (i + j) = dn (i - j) = TRUE;
  5165.  
  5166.        y la condición segura del pseudocódigo:
  5167.  
  5168.          f (i) && di (i + j) && dn (i - j)
  5169. */
  5170.  
  5171. /* Ficheros a incluir: */
  5172.  
  5173. #include <stdio.h>  /* printf () */
  5174. #include <conio.h>  /* getch () */
  5175.  
  5176. /* Macros: */
  5177.  
  5178. #define BOOLEAN int
  5179. #define TRUE    1
  5180. #define FALSE   0
  5181.  
  5182. /* Variables globales: */
  5183.  
  5184. BOOLEAN acertado;
  5185.  
  5186. int posiciones_en_columna[8];
  5187. BOOLEAN reina_en_fila[8];
  5188. BOOLEAN reina_en_diagonal_normal[15];
  5189. BOOLEAN reina_en_diagonal_inversa[15];
  5190.  
  5191. #define c(i) posiciones_en_columna[(i)-1]      /* rango de índice: 1..8  */
  5192. #define f(i) reina_en_fila[(i)-1]              /* rango de índice: 1..8  */
  5193. #define dn(i) reina_en_diagonal_normal[(i)+7]  /* rango de índice: -7..7 */
  5194. #define di(i) reina_en_diagonal_inversa[(i)-2] /* rango de índice: 2..16 */
  5195.  
  5196. /* Prototipos de las funciones: */
  5197.  
  5198. void proceso (void);
  5199. void ensayar (int i);
  5200.  
  5201. /* Definiciones de las funciones: */
  5202.  
  5203. void main (void)
  5204. {
  5205.   printf ("\n\nPROBLEMA DE LAS OCHO REINAS:\n  ");
  5206.   proceso ();
  5207.   printf ("\n\nPulsa cualquier tecla para finalizar. ");
  5208.   getch ();
  5209. }
  5210.  
  5211. void proceso (void)
  5212. {
  5213.   register int i;
  5214.  
  5215.   for (i = 1; i <= 8; i++)
  5216.     f (i) = TRUE;
  5217.   for (i = 2; i <= 16; i++)
  5218.     di (i) = TRUE;
  5219.   for (i = -7; i <= 7; i++)
  5220.     dn (i) = TRUE;
  5221.  
  5222.   ensayar (1);
  5223.  
  5224.   if (acertado)
  5225.     for (printf ("\n\nLA SOLUCION ES:\n\n"), i = 1; i <= 8; i++)
  5226.       {
  5227.         for (j = 1; j <= 8; j++)
  5228.           printf ("%2d", c (j) == i ? 1 : 0);
  5229.         printf ("\n");
  5230.       }
  5231.   else
  5232.     printf ("\n\nNO HAY SOLUCION.\n");
  5233. }
  5234.  
  5235. void ensayar (int i)
  5236. {
  5237.   int j = 0;
  5238.  
  5239.   do
  5240.     {
  5241.       j++;
  5242.       acertado = FALSE;
  5243.       if (f (j) && di (i + j) && dn (i - j))
  5244.         {
  5245.           c (i) = j;
  5246.           f (j) = di (i + j) = dn (i - j) = FALSE;
  5247.           if (i < 8)
  5248.             {
  5249.               ensayar (i + 1);
  5250.               if (! acertado)
  5251.                 f (j) = di (i + j) = dn (i - j) = TRUE;
  5252.             }
  5253.           else
  5254.             acertado = TRUE;
  5255.         }
  5256.     } while (! acertado && j != 8);
  5257. }
  5258. ende
  5259. begine " PROBLEMA DE LAS OCHO REINAS AMPLIADO "
  5260. /*
  5261.   Este programa es una ampliación del anterior. En el anterior encontrábamos
  5262.   una solución, en éste vamos encontrar e imprimir todas las soluciones.
  5263.  
  5264.   Para ello lo único que hay que hacer es modificar sensiblemente la
  5265.   función ensayar(). El nuevo pseudocódigo para esta función es:
  5266.  
  5267.     <PRINCIPIO ensayar> (i: entero)
  5268.     Entorno:
  5269.       j es una variable entera
  5270.     Lógica:
  5271.       PARA j <- 1 HASTA dimensión tablero CON INCREMENTO 1 HACER
  5272.         seleccionar el candidato j-ésimo
  5273.         SI aceptable ENTONCES
  5274.           anotarlo
  5275.           SI i < dimensión tablero ENTONCES
  5276.             LLAMAR ensayar (i + 1)
  5277.           SINO
  5278.             imprimir solución
  5279.           FINSI
  5280.           cancelar anotación
  5281.         FINSI
  5282.       FINPARA
  5283.     <FIN>
  5284. */
  5285.  
  5286. /* Ficheros a incluir: */
  5287.  
  5288. #include <stdio.h>  /* printf () */
  5289. #include <conio.h>  /* getch ()  */
  5290. #include <stdlib.h> /* exit ()   */
  5291.  
  5292. /* Macros: */
  5293.  
  5294. #define BOOLEAN int
  5295. #define TRUE    1
  5296. #define FALSE   0
  5297.  
  5298. #define ESC 27
  5299.  
  5300. /* Variables globales: */
  5301.  
  5302. int posiciones_en_columna[8];
  5303. BOOLEAN reina_en_fila[8];
  5304. BOOLEAN reina_en_diagonal_normal[15];
  5305. BOOLEAN reina_en_diagonal_inversa[15];
  5306.  
  5307. #define c(i) posiciones_en_columna[(i)-1]      /* rango de índice: 1..8 */
  5308. #define f(i) reina_en_fila[(i)-1]              /* rango de índice: 1..8 */
  5309. #define dn(i) reina_en_diagonal_normal[(i)+7]  /* rango de índice: -7..7 */
  5310. #define di(i) reina_en_diagonal_inversa[(i)-2] /* rango de índice: 2..16 */
  5311.  
  5312. /* Prototipos de las funciones: */
  5313.  
  5314. void proceso (void);
  5315. void escribir_solucion (void);
  5316. void ensayar (int i);
  5317.  
  5318. /* Definiciones de las funciones: */
  5319.  
  5320. void main (void)
  5321. {
  5322.   printf ("\n\nENCONTRAR TODAS LAS SOLUCIONES DEL PROBLEMA DE LAS OCHO REINAS:\n  ");
  5323.   proceso ();
  5324.   printf ("\n\nPulsa cualquier tecla para finalizar. ");
  5325.   getch ();
  5326. }
  5327.  
  5328. void proceso (void)
  5329. {
  5330.   register int i;
  5331.  
  5332.   for (i = 1; i <= 8; i++)
  5333.     f (i) = TRUE;
  5334.   for (i = 2; i <= 16; i++)
  5335.     di (i) = TRUE;
  5336.   for (i = -7; i <= 7; i++)
  5337.     dn (i) = TRUE;
  5338.  
  5339.   printf ("\nPulsar ESC para salir o cualquier otra tecla para ver siguiente solución.\n");
  5340.   ensayar (1);
  5341. }
  5342.  
  5343. void escribir_solucion (void)
  5344. {
  5345.   register int i, j;
  5346.   static numero_solucion = 0;
  5347.  
  5348.   printf ("\nSolución %d:\n", ++numero_solucion);
  5349.   for (i = 1; i <= 8; i++)
  5350.     {
  5351.       for (j = 1; j <= 8; j++)
  5352.         printf ("%2d", c (j) == i ? 1 : 0);
  5353.       printf ("\n");
  5354.     }
  5355.   if (getch () == ESC)
  5356.     exit (1);
  5357. }
  5358.  
  5359. void ensayar (int i)
  5360. {
  5361.   register int j;
  5362.  
  5363.   for (j = 1; j <= 8; j++)
  5364.     if (f (j) && di (i + j) && dn (i - j))
  5365.       {
  5366.         c (i) = j;
  5367.         f (j) = di (i + j) = dn (i - j) = FALSE;
  5368.         if (i < 8)
  5369.           ensayar (i + 1);
  5370.         else
  5371.           escribir_solucion ();
  5372.         f (j) = di (i + j) = dn (i - j) = TRUE;
  5373.       }
  5374. }
  5375. ende
  5376. end lección 6
  5377.  
  5378. ; LECCION 7
  5379. begin
  5380. begine " 21 FUNCIONES DE MANIPULACION DE CADENAS DE CARACTERES "
  5381. /*
  5382.   Este programa consta de 21 funciones sobre manipulación de punteros a cadenas
  5383.   de arrays y la función main para probar las funciones mencionadas.
  5384.  
  5385.   Hay dos cosas importantes que comentar:
  5386.  
  5387.     1) En este ejemplo hablamos de strings en vez de cadenas de caracteres
  5388.       porque es la denominación más usada en C.
  5389.  
  5390.     2) La lógica (la estructura) de este ejemplo es exactamente la misma que
  5391.       la del último ejemplo de la lección anterior. Ver el comentario al
  5392.       principio de dicho ejemplo si no se comprende algo de éste (como la
  5393.       compilación condicional #if __TURBOC__, la librería alloc.h, la
  5394.       estructura de la función main(), ...
  5395. */
  5396.  
  5397. /* Ficheros a incluir: */
  5398.  
  5399. #include <stdio.h> /* printf (), puts (), scanf (), gets () */
  5400. #include <alloc.h> /* calloc(), free () */
  5401. #include <conio.h> /* clrscr () si TURBO C, putch (), getch () */
  5402. #include <dos.h>   /* int86 (), union REGS */
  5403.  
  5404. /* Compilación condicional: */
  5405.  
  5406. #ifdef __TURBOC__
  5407.   #define borrar_pantalla clrscr
  5408. #else
  5409.   void borrar_pantalla (void) /* sólo hay que saber de esta función, en este momento, ... */
  5410.   {                           /* ... que borra la pantalla, no qué significa estas sentecias */
  5411.     union REGS regs;
  5412.     regs.h.ah = 6; /* código de desplazamiento de la pantalla */
  5413.     regs.h.al = 0; /* código de borrar la pantalla */
  5414.     regs.h.ch = 0; /* fila inicial */
  5415.     regs.h.cl = 0; /* columna inicial */
  5416.     regs.h.dh = 24; /* fila final */
  5417.     regs.h.dl = 79; /* columna final */
  5418.     regs.h.bh = 7; /* la línea de borrado en negra */
  5419.     int86 (0x10, ®s, ®s);
  5420.     regs.h.ah = 2; /* función de direccionamiento del cursor */
  5421.     regs.h.dl = 0; /* coordenada de la columna */
  5422.     regs.h.dh = 0; /* coordenada de la fila */
  5423.     regs.h.bh = 0; /* página de vídeo */
  5424.     int86 (0x10, ®s, ®s);
  5425.   }
  5426. #endif
  5427.  
  5428. /* Prototipos de funciones utilizadas: */
  5429.  
  5430. int longitud_string (char *s);
  5431. int longitud_string_version_2 (char *s);
  5432. int longitud_string_version_3 (char *s);
  5433. int indice_de_caracter_en_string (char *s, char caracter);
  5434. int contador_de_caracter_en_string (char *s, char c);
  5435. void reemplazar_caracter_en_string (char *s, char viejo_caracter,
  5436.                                     char nuevo_caracter);
  5437. void string_a_mayuscula (char *s);
  5438. void string_a_minuscula (char *s);
  5439. void copiar_rapido_string (char *s1, char *s2);
  5440. int copiar_string_con_chequeo_de_indie (char *s1, char *s2,
  5441.                                         int numero_maximo_de_caracteres);
  5442. void copiar_string_recursivo (char *s1, char *s2);
  5443. void aniadir_rapido_string (char *s1, char *s2);
  5444. int strings_iguales (char *s1, char *s2, int ignorar_caso);
  5445. int comparar_strings (char *s1, char *s2, int ignorar_caso);
  5446. int primera_diferencia_entre_strings (char *s1, char *s2, int ignorar_caso);
  5447. int indice_de_substring_en_string (char *substring, char *string);
  5448. int contador_de_string (char *substring, char *string);
  5449. int ascii_a_int (char *string, int *valor);
  5450. void int_a_ascii (int valor, char *string);
  5451. int comparar_strings_sin_ignorar_caso (char *s1, char *s2);
  5452. char *salvar_string (char *s);
  5453.  
  5454. /* Definición de funciones: */
  5455.  
  5456. /*
  5457.   Función principal.
  5458. */
  5459.  
  5460. void main (void)
  5461. {
  5462.   #define ESC 27
  5463.   #define CARACTER_A_MAYUSCULA(c) ((c) >= 'a' && (c) <= 'z' ? ((c) & ~32) : (c))
  5464.   #define ENTRE(x,x1,x2) ((x) >= (x1) && (x) <= (x2))
  5465.  
  5466.   #define leer_string(string,nombre_string) \
  5467.     { printf ("\n%s (35 caracteres máximo): ", nombre_string); \
  5468.       scanf ("%35s", string); }
  5469.   #define leer_s() leer_string(s,"s")
  5470.   #define leer_s1() leer_string(s1,"s1")
  5471.   #define leer_s2() leer_string(s2,"s2")
  5472.   #define leer_valor_entero(variable, nombre_variable) \
  5473.     { printf ("\n%s: ", nombre_variable); \
  5474.     scanf ("%d", &variable); }
  5475.   #define leer_caracter(caracter,nombre_caracter) \
  5476.     { printf ("\n%s: ", nombre_caracter); caracter = getche (); putch ('\n'); }
  5477.   #define leer_c() leer_caracter(c,"s")
  5478.   #define leer_c1() leer_caracter(c1,"s1")
  5479.   #define leer_c2() leer_caracter(c2,"s2")
  5480.  
  5481.   #define escribir_string(string,nombre_string) \
  5482.     printf ("\n%s: %-35s\n", nombre_string, string);
  5483.   #define escribir_s() escribir_string(s,"s")
  5484.   #define escribir_s1() escribir_string(s1,"s1")
  5485.   #define escribir_s2() escribir_string(s2, "s2")
  5486.   #define escribir_valor_devuelto() printf ("\nvalor_devuelto: %d\n", valor_devuelto);
  5487.   #define escribir_valor_entero(variable, nombre_variable) printf ("\n%s: %d\n", nombre_variable, variable);
  5488.   #define escribir_titulo(s) printf ("FUNCION:\n\n%s\n", s)
  5489.   #define escribir_entrada() printf ("\n\nVALORES DE ENTRADA:\n");
  5490.   #define escribir_salida() printf ("\n\nVALORES DE SALIDA:\n");
  5491.  
  5492.   char ch1, ch2;
  5493.   register int i;
  5494.   int numero_de_letras, numero_de_cifras;
  5495.   char s[35], s1[35], s2[35], c, c1, c2;
  5496.   int valor_devuelto;
  5497.  
  5498.   char *funciones[] =
  5499.     {
  5500.       "longitud_string",
  5501.       "longitud_string_version_2",
  5502.       "longitud_string_version_3",
  5503.       "indice_de_caracter_en_string",
  5504.       "contador_de_caracter_en_string",
  5505.       "reemplazar_caracter_en_string",
  5506.       "string_a_mayuscula",
  5507.       "string_a_minuscula",
  5508.       "copiar_rapido_string",
  5509.       "copiar_string_con_chequeo_de_indice",
  5510.       "copiar_string_recursivo",
  5511.       "aniadir_rapido_string",
  5512.       "strings_iguales",
  5513.       "comparar_strings",
  5514.       "comparar_strings_sin_ignorar_caso",
  5515.       "primera_diferencia_entre_strings",
  5516.       "indice_de_substring_en_string",
  5517.       "contador_de_string",
  5518.       "ascii_a_int",
  5519.       "int_a_ascii",
  5520.       "salvar_string",
  5521.       NULL
  5522.     };
  5523.  
  5524.   do
  5525.     {
  5526.       borrar_pantalla ();
  5527.  
  5528.       printf ("FUNCIONES PARA PROBAR:\n\n");
  5529.       for (i = 0; funciones[i] != NULL; i++)
  5530.         {
  5531.           printf ("%c.- %-35.35s", 'A' + i > 'Z' ? '0' + i - ('Z' - 'A' + 1) :
  5532.                    'A' + i, funciones[i]);
  5533.           if (funciones[i+1] != NULL)
  5534.             {
  5535.               i++;
  5536.               printf (" %c.- %-35.35s\n", 'A' + i > 'Z' ? '0' + i - ('Z' - 'A' + 1) :
  5537.                        'A' + i, funciones[i]);
  5538.             }
  5539.           else
  5540.             printf ("\n");
  5541.         }
  5542.  
  5543.       numero_de_letras = i;
  5544.       numero_de_cifras = numero_de_letras > 'Z' - 'A' + 1 ? i - ('Z' - 'A' + 1) : 0;
  5545.       printf ("\nTecla letra correspondiente a función a probar (ESC para salir): ");
  5546.  
  5547.       do
  5548.         {
  5549.           ch1 = getch (); /* lee carácter correspondiente a opción */
  5550.           ch1 = CARACTER_A_MAYUSCULA (ch1); /* convierte carácter a mayúscula */
  5551.         } while (ch1 != ESC && ! ENTRE (ch1, 'A', 'A' + numero_de_letras - 1) &&
  5552.                  (numero_de_cifras >= 0 && ! ENTRE (ch1, '0', '0' + numero_de_cifras - 1)));
  5553.  
  5554.       if (ch1 != ESC)
  5555.         do
  5556.           {
  5557.             borrar_pantalla ();
  5558.  
  5559.             switch (ch1)
  5560.               {
  5561.                 case 'A':
  5562.                   escribir_titulo ("int longitud_string (char *s);");
  5563.                   escribir_entrada ();
  5564.                   leer_s ();
  5565.                   valor_devuelto = longitud_string (s);
  5566.                   escribir_salida ();
  5567.                   escribir_valor_devuelto ();
  5568.                   break;
  5569.  
  5570.                 case 'B':
  5571.                   escribir_titulo ("int longitud_string_version_2 (char *s);");
  5572.                   escribir_entrada ();
  5573.                   leer_s ();
  5574.                   valor_devuelto = longitud_string_version_2 (s);
  5575.                   escribir_salida ();
  5576.                   escribir_valor_devuelto ();
  5577.                   break;
  5578.  
  5579.                 case 'C':
  5580.                   escribir_titulo ("int longitud_string_version_3 (char *s);");
  5581.                   escribir_entrada ();
  5582.                   leer_s ();
  5583.                   valor_devuelto = longitud_string_version_3 (s);
  5584.                   escribir_salida ();
  5585.                   escribir_valor_devuelto ();
  5586.                   break;
  5587.  
  5588.                 case 'D':
  5589.                   escribir_titulo ("int indice_de_caracter_en_string (char *s, char c);");
  5590.                   escribir_entrada ();
  5591.                   leer_s ();
  5592.                   leer_c ();
  5593.                   valor_devuelto = indice_de_caracter_en_string (s, c);
  5594.                   escribir_salida ();
  5595.                   escribir_valor_devuelto ();
  5596.                   break;
  5597.  
  5598.                 case 'E':
  5599.                   escribir_titulo ("int contador_de_caracter_en_string (char *s, char c);");
  5600.                   escribir_entrada ();
  5601.                   leer_s ();
  5602.                   leer_c ();
  5603.                   valor_devuelto = contador_de_caracter_en_string (s, c);
  5604.                   escribir_salida ();
  5605.                   escribir_valor_devuelto ();
  5606.                   break;
  5607.  
  5608.                 case 'F':
  5609.                   escribir_titulo ("void reemplazar_caracter_en_string (char *s, char viejo_caracter,\n"
  5610.                                    "                                    char nuevo_caracter);");
  5611.                   escribir_entrada ();
  5612.                   leer_s ();
  5613.                   leer_caracter (c1, "viejo_caracter");
  5614.                   leer_caracter (c2, "nuevo_caracter");
  5615.                   reemplazar_caracter_en_string (s, c1, c2);
  5616.                   escribir_salida ();
  5617.                   escribir_s ();
  5618.                   break;
  5619.  
  5620.                 case 'G':
  5621.                   escribir_titulo ("void string_a_mayuscula (char *s);");
  5622.                   escribir_entrada ();
  5623.                   leer_s ();
  5624.                   string_a_mayuscula (s);
  5625.                   escribir_salida ();
  5626.                   escribir_s ();
  5627.                   break;
  5628.  
  5629.                 case 'H':
  5630.                   escribir_titulo ("void string_a_minuscula (char *s);");
  5631.                   escribir_entrada ();
  5632.                   leer_s ();
  5633.                   string_a_minuscula (s);
  5634.                   escribir_salida ();
  5635.                   escribir_s ();
  5636.                   break;
  5637.  
  5638.                 case 'I':
  5639.                   escribir_titulo ("void copiar_rapido_string (char *s1, char *s2);");
  5640.                   escribir_entrada ();
  5641.                   leer_s1 ();
  5642.                   copiar_rapido_string (s1, s2);
  5643.                   escribir_salida ();
  5644.                   escribir_s2 ();
  5645.                   break;
  5646.  
  5647.                 case 'J':
  5648.                   {
  5649.                     int numero_maximo_de_caracteres;
  5650.                     escribir_titulo ("int copiar_string_con_chequeo_de_indice (char *s1, char *s2,\n"
  5651.                                      "                                         int numero_maximo_de_caracteres);");
  5652.                     escribir_entrada ();
  5653.                     leer_s1 ();
  5654.                     leer_valor_entero (numero_maximo_de_caracteres, "numero_maximo_de_caracteres");
  5655.                     valor_devuelto = copiar_string_con_chequeo_de_indice (s1, s2, numero_maximo_de_caracteres);
  5656.                     escribir_salida ();
  5657.                     escribir_s2 ();
  5658.                     escribir_valor_devuelto ();
  5659.                   }
  5660.                   break;
  5661.  
  5662.                 case 'K':
  5663.                   escribir_titulo ("void copiar_string_recursivo (char *s1, char *s2);");
  5664.                   escribir_entrada ();
  5665.                   leer_s1 ();
  5666.                   copiar_rapido_string (s1, s2);
  5667.                   escribir_salida ();
  5668.                   escribir_s2 ();
  5669.                   break;
  5670.  
  5671.                 case 'L':
  5672.                   escribir_titulo ("void aniadir_rapido_string (char *s1, char *s2);");
  5673.                   escribir_entrada ();
  5674.                   leer_string (s1, "string_fuente s1");
  5675.                   leer_string (s2, "string_destino s2");
  5676.                   aniadir_rapido_string (s1, s2);
  5677.                   escribir_salida ();
  5678.                   escribir_s2 ();
  5679.                   break;
  5680.  
  5681.                 case 'M':
  5682.                   {
  5683.                     int ignorar_caso;
  5684.                     escribir_titulo ("int strings_iguales (char *s1, char *s2, int ignorar_caso);");
  5685.                     escribir_entrada ();
  5686.                     leer_s1 ();
  5687.                     leer_s2 ();
  5688.                     leer_valor_entero (ignorar_caso, "ignorar_caso");
  5689.                     valor_devuelto = strings_iguales (s1, s2, ignorar_caso);
  5690.                     escribir_salida ();
  5691.                     escribir_valor_devuelto ();
  5692.                   }
  5693.                   break;
  5694.  
  5695.                 case 'N':
  5696.                   {
  5697.                     int ignorar_caso;
  5698.                     escribir_titulo ("int comparar_strings (char *s1, char *s2, int ignorar_caso);");
  5699.                     escribir_entrada ();
  5700.                     leer_s1 ();
  5701.                     leer_s2 ();
  5702.                     leer_valor_entero (ignorar_caso, "ignorar_caso");
  5703.                     valor_devuelto = comparar_strings (s1, s2, ignorar_caso);
  5704.                     escribir_salida ();
  5705.                     escribir_valor_devuelto ();
  5706.                   }
  5707.                   break;
  5708.  
  5709.                 case 'O':
  5710.                   escribir_titulo ("int comparar_strings_sin_ignorar_caso (char *s1, char *s2);");
  5711.                   escribir_entrada ();
  5712.                   leer_s1 ();
  5713.                   leer_s2 ();
  5714.                   valor_devuelto = comparar_strings_sin_ignorar_caso (s1, s2);
  5715.                   escribir_salida ();
  5716.                   escribir_valor_devuelto ();
  5717.                   break;
  5718.  
  5719.                 case 'P':
  5720.                   {
  5721.                     int ignorar_caso;
  5722.                     escribir_titulo ("int primera_diferencia_entre_strings (char *s1, char *s2, int ignorar_caso);");
  5723.                     escribir_entrada ();
  5724.                     leer_s1 ();
  5725.                     leer_s2 ();
  5726.                     leer_valor_entero (ignorar_caso, "ignorar_caso");
  5727.                     valor_devuelto = primera_diferencia_entre_strings (s1, s2, ignorar_caso);
  5728.                     escribir_salida ();
  5729.                     escribir_valor_devuelto ();
  5730.                   }
  5731.                   break;
  5732.  
  5733.                 case 'Q':
  5734.                   escribir_titulo ("int indice_de_substring_en_string (char *substring, char *string);");
  5735.                   escribir_entrada ();
  5736.                   leer_string (s1, "substring");
  5737.                   leer_string (s2, "string");
  5738.                   valor_devuelto = indice_de_substring_en_string (s1, s2);
  5739.                   escribir_salida ();
  5740.                   escribir_valor_devuelto ();
  5741.                   break;
  5742.  
  5743.                 case 'R':
  5744.                   escribir_titulo ("int contador_de_string (char *substring, char *string);");
  5745.                   escribir_entrada ();
  5746.                   leer_string (s1, "substring");
  5747.                   leer_string (s2, "string");
  5748.                   valor_devuelto = contador_de_string (s1, s2);
  5749.                   escribir_salida ();
  5750.                   escribir_valor_devuelto ();
  5751.                   break;
  5752.  
  5753.                 case 'S':
  5754.                   {
  5755.                     int valor;
  5756.                     escribir_titulo ("int ascii_a_int (char *string, int *valor);");
  5757.                     escribir_entrada ();
  5758.                     leer_string (s, "string");
  5759.                     valor_devuelto = ascii_a_int (s, &valor);
  5760.                     escribir_salida ();
  5761.                     escribir_valor_entero (valor, "valor");
  5762.                     escribir_valor_devuelto ();
  5763.                   }
  5764.                   break;
  5765.  
  5766.                 case 'T':
  5767.                   {
  5768.                     int valor;
  5769.                     escribir_titulo ("void int_a_ascii (int valor, char *string);");
  5770.                     escribir_entrada ();
  5771.                     leer_valor_entero (valor, "valor");
  5772.                     int_a_ascii (valor, s);
  5773.                     escribir_salida ();
  5774.                     escribir_string (s, "string");
  5775.                   }
  5776.                   break;
  5777.  
  5778.                 case 'U':
  5779.                   {
  5780.                     char *string;
  5781.                     escribir_titulo ("char *salvar_string (char *s);");
  5782.                     escribir_entrada ();
  5783.                     leer_s ();
  5784.                     string = salvar_string (s);
  5785.                     escribir_salida ();
  5786.                     escribir_string (string, "valor devuelto");
  5787.                     free (string);
  5788.                   }
  5789.                   break;
  5790.               }
  5791.  
  5792.            while (kbhit ()) /* vacía buffer de teclas */
  5793.              getch ();
  5794.  
  5795.            do
  5796.              {
  5797.                printf ("\n\n\n¿Deseas probar otra vez esta función (S/N)? ");
  5798.                ch2 = getch ();
  5799.                ch2 = CARACTER_A_MAYUSCULA (ch2);
  5800.              } while (ch2 != 'S' && ch2 != 'N');
  5801.  
  5802.            while (kbhit ()) /* vacía buffer de teclas */
  5803.              getch ();
  5804.  
  5805.           } while (ch2 == 'S');
  5806.  
  5807.     } while (ch1 != ESC);
  5808. }
  5809.  
  5810. /*
  5811.   Devuelve el número de caracteres en un string.
  5812. */
  5813.  
  5814. int longitud_string (char *s)
  5815. {
  5816.   int longitud = 0;
  5817.  
  5818.   while (*s++)
  5819.     longitud++;
  5820.  
  5821.   return (longitud);
  5822. }
  5823.  
  5824. /*
  5825.   Devuelve la longitud del string s.
  5826.   Esta versión está basada en la sustracción de punteros.
  5827. */
  5828.  
  5829. int longitud_string_version_2 (char *s)
  5830. {
  5831.   char *p = s;
  5832.  
  5833.   while (*p)
  5834.     p++;
  5835.  
  5836.   return (p - s);
  5837. }
  5838.  
  5839. /*
  5840.   Devuelve el número de caracteres del string s.
  5841.   Este cáculo lo hace de forma recursiva.
  5842. */
  5843.  
  5844. int longitud_string_version_3 (char *s)
  5845. {
  5846.   return (*s ? 1 + longitud_string_version_3 (++s) : 0);
  5847. }
  5848.  
  5849. /*
  5850.   Devuelve el índice de la primera ocurrencia del carácter especificado
  5851.   en el string dado. Si dicho carácter no se encuentra en el string,
  5852.   entonces devuelve -1.
  5853. */
  5854.  
  5855. int indice_de_caracter_en_string (char *s, char c)
  5856. {
  5857.   int contador, posicion = -1;
  5858.  
  5859.   for (contador = 0; *s && posicion == -1; contador++)
  5860.     if (*s++ == c)
  5861.       posicion = contador;
  5862.  
  5863.   return (posicion);
  5864. }
  5865.  
  5866. /*
  5867.   Devuelve el número de ocurrencias del carácter c en el string s.
  5868. */
  5869.  
  5870. int contador_de_caracter_en_string (char *s, char c)
  5871. {
  5872.   int contador = 0;
  5873.  
  5874.   while (*s)
  5875.     if (*s++ == c)
  5876.       contador++;
  5877.  
  5878.   return (contador);
  5879. }
  5880.  
  5881. /*
  5882.   Reemplaza cada ocurrencia de viejo_caracter en el string s con el
  5883.   carácter contenido en nuevo_caracter.
  5884. */
  5885.  
  5886. void reemplazar_caracter_en_string (char *s, char viejo_caracter,
  5887.                                     char nuevo_caracter)
  5888. {
  5889.   while (*s)
  5890.     if (*s == viejo_caracter)
  5891.       *s++ = nuevo_caracter;
  5892.     else
  5893.       s++;
  5894. }
  5895.  
  5896. /*
  5897.   Convierte un string a carácteres en mayúsculas.
  5898. */
  5899.  
  5900. void string_a_mayuscula (char *s)
  5901. {
  5902.   while (*s)
  5903.     if (*s >= 'a' && *s <= 'z')
  5904.       *s++ &= ~32;
  5905.     else
  5906.       s++;
  5907. }
  5908.  
  5909. /*
  5910.   Convierte un string a carácteres en minúsculas.
  5911. */
  5912.  
  5913. void string_a_minuscula (char *s)
  5914. {
  5915.   while (*s)
  5916.     if (*s >= 'A' && *s <= 'Z')
  5917.       *s++ |= 32;
  5918.     else
  5919.       s++;
  5920. }
  5921.  
  5922. /*
  5923.   Copia el contenido del string s1 en el string s2.
  5924.   No chequea índices.
  5925. */
  5926.  
  5927. void copiar_rapido_string (char *s1, char *s2)
  5928. {
  5929.   while (*s2++ = *s1++)
  5930.     ;
  5931. }
  5932.  
  5933. /*
  5934.   Copia el string fuente s1 en el string destino s2.
  5935.   Se chequea índice devolviendo 1 si se intenta sobrepasar índice máximo o
  5936.   0 si la copia ha sido completa.
  5937. */
  5938.  
  5939. int copiar_string_con_chequeo_de_indice (char *s1, char *s2,
  5940.                                          int numero_maximo_de_caracteres)
  5941. {
  5942.   register int i;
  5943.  
  5944.   numero_maximo_de_caracteres--; /* deja espacio para nulo */
  5945.  
  5946.   for (i = 0; (*s2++ = *s1++) && (i < numero_maximo_de_caracteres); i++)
  5947.     ;
  5948.  
  5949.   if (i == numero_maximo_de_caracteres && *s1) /* ver si quedan caracteres en s1 */
  5950.     {
  5951.       *s2 = '\0';
  5952.       return (1);
  5953.     }
  5954.   else
  5955.     return (0);
  5956. }
  5957.  
  5958. /*
  5959.   Copia el contenido del string fuente s1 al string de destino s2.
  5960.   Esto lo hace de forma recursiva. No chequea índices.
  5961. */
  5962.  
  5963. void copiar_string_recursivo (char *s1, char *s2)
  5964. {
  5965.   if (*s2 = *s1)
  5966.     copiar_string_recursivo (++s1, ++s2);
  5967. }
  5968.  
  5969. /*
  5970.   Añade el contenido del string fuente s1 al string destino s2.
  5971.   No chequea índices.
  5972. */
  5973.  
  5974. void aniadir_rapido_string (char *s1, char *s2)
  5975. {
  5976.   while (*s2) /* encuentra el final de s2 */
  5977.     s2++;
  5978.  
  5979.   while (*s2++ = *s1++) /* lo añade */
  5980.     ;
  5981. }
  5982.  
  5983. /*
  5984.   Devuelve 1 si los strings s1 y s2 son iguales, en cualquier otro caso
  5985.   devuelve 0. Soporta proceso sensitivo al caso.
  5986. */
  5987.  
  5988. int strings_iguales (char *s1, char *s2, int ignorar_caso)
  5989. {
  5990.   char c1, c2;
  5991.  
  5992.   for (; *s1 && *s2; s1++, s2++)
  5993.     if (*s1 != *s2)
  5994.       {
  5995.         if (ignorar_caso)
  5996.           {
  5997.             c1 = *s1 >= 'a' && *s1 <= 'z' ? *s1 & ~32 : *s1;
  5998.             c2 = *s2 >= 'a' && *s2 <= 'z' ? *s2 & ~32 : *s2;
  5999.             if (c1 != c2)
  6000.               break;
  6001.           }
  6002.         else
  6003.           break;
  6004.       }
  6005.  
  6006.   if (*s1 || *s2)
  6007.     return (0);
  6008.   else
  6009.     return (1);
  6010. }
  6011.  
  6012. /*
  6013.   Compara los strings especificados. Devuelve 1 si s1 > s2, 2 si s2 > s1
  6014.   y 0 si los strings son iguales. Suporta proceso sensitivo al caso.
  6015. */
  6016.  
  6017. int comparar_strings (char *s1, char *s2, int ignorar_caso)
  6018. {
  6019.   char c1, c2;
  6020.   int resultado = 0; /* 0 igual, 1 s1 mayor, 2 s2 mayor */
  6021.  
  6022.   for (; *s1 && *s2; s1++, s2++)
  6023.     if (*s1 != *s2)
  6024.       {
  6025.         if (ignorar_caso)
  6026.           {
  6027.             c1 = *s1 >= 'a' && *s1 <= 'z' ? *s1 & ~32 : *s1;
  6028.             c2 = *s2 >= 'a' && *s2 <= 'z' ? *s2 & ~32 : *s2;
  6029.             if (c1 != c2)
  6030.               {
  6031.                 if (c1 > c2)
  6032.                   resultado = 1;
  6033.                 else
  6034.                   resultado = 2;
  6035.                 break;
  6036.               }
  6037.           }
  6038.         else
  6039.             {
  6040.             if (*s1 > *s2)
  6041.               resultado = 1;
  6042.             else
  6043.               resultado = 2;
  6044.             break;
  6045.           }
  6046.       }
  6047.  
  6048.   if (resultado == 0)
  6049.     {
  6050.       if (*s1 == *s2)
  6051.         resultado = 0;
  6052.       else if (*s1)
  6053.         resultado = 1;
  6054.       else
  6055.         resultado = 2;
  6056.     }
  6057.  
  6058.   return (resultado);
  6059. }
  6060.  
  6061. /*
  6062.   Devuelve uno de los siguientes valores:
  6063.      < 1 si s1 es menor que s2
  6064.     == 0 si s1 y s2 son iguales
  6065.      > 1 si s1 es mayor que s2
  6066.  
  6067.   Tiene en cuenta el caso de los caracteres (por ejemplo: 'a' != 'A').
  6068.  
  6069.   Los valores distintos de 0 que se devuelven corresponden a la diferencia
  6070.   entre los primeros caracteres encontrados que son distintos.
  6071. */
  6072.  
  6073. int comparar_strings_sin_ignorar_caso (char *s1, char *s2)
  6074. {
  6075.   while (*s1)
  6076.     if (*s1 - *s2)
  6077.       return (*s1 - *s2);
  6078.     else
  6079.       {
  6080.         s1++;
  6081.         s2++;
  6082.       }
  6083.  
  6084.   return ('\0'); /* iguales */
  6085. }
  6086.  
  6087. /*
  6088.   Devuelve la posición del índice del primer carácter que difiere entre
  6089.   s1 y s2. Si los strings son iguales, devuelve el valor -1.
  6090. */
  6091.  
  6092. int primera_diferencia_entre_strings (char *s1, char *s2, int ignorar_caso)
  6093. {
  6094.   register int i;
  6095.   char c1, c2;
  6096.  
  6097.   for (i = 0; *s1 && *s2; s1++, s2++, i++)
  6098.     if (*s1 != *s2)
  6099.       {
  6100.         if (ignorar_caso)
  6101.           {
  6102.             c1 = *s1 >= 'a' && *s1 <= 'z' ? *s1 & ~32 : *s1;
  6103.             c2 = *s2 >= 'a' && *s2 <= 'z' ? *s2 & ~32 : *s2;
  6104.             if (c1 != c2)
  6105.               break;
  6106.           }
  6107.       }
  6108.  
  6109.   if (*s1 || *s2)
  6110.     return (1);
  6111.   else
  6112.     return (-1);
  6113. }
  6114.  
  6115. /*
  6116.   Devuelve el índice de comienzo de substring dentro de string o el valor -1
  6117.   si substring no se encuentra en string.
  6118. */
  6119.  
  6120. int indice_de_substring_en_string (char *substring, char *string)
  6121. {
  6122.   char *substr, *str, *comienzo = string;
  6123.  
  6124.   while (*string)
  6125.     for (str = string++, substr = substring; *str == *substr; str++, substr++)
  6126.       if (! *(substr+1)) /* final de substr */
  6127.         return (string - comienzo - 1);
  6128.  
  6129.   return (-1); /* substr no encontrado */
  6130. }
  6131.  
  6132. /*
  6133.   Devuelve el número de ocurrencias de substring en string o el valor 0 si
  6134.   substring no se encuentra en string.
  6135. */
  6136.  
  6137. int contador_de_string (char *substring, char *string)
  6138. {
  6139.   char *substr, *str;
  6140.  
  6141.   int contador = 0;
  6142.  
  6143.   while (*string)
  6144.     for (str = string++, substr = substring; *str == *substr; str++, substr++)
  6145.       if (! *(substr+1)) /* final de substr */
  6146.         ++contador;
  6147.  
  6148.   return (contador);
  6149. }
  6150.  
  6151. /*
  6152.   Convierte un string con caracteres que representan números a un valor
  6153.   numérico de tipo entero. Si el string contiene caracteres no válidos,
  6154.   se devuelve -1.
  6155. */
  6156.  
  6157. int ascii_a_int (char *string, int *valor)
  6158. {
  6159.   int signo = 1; /* -1 si valor negativo */
  6160.  
  6161.   *valor = 0;
  6162.  
  6163.   while (*string == ' ') /* salta blancos */
  6164.     string++;
  6165.  
  6166.   if (*string == '-' || *string == '+')
  6167.     signo = (*string++ == '-') ? -1 : 1;
  6168.  
  6169.   while (*string)
  6170.     if ((*string >= '0') && (*string <= '9'))
  6171.       *valor = (*valor * 10) + (*string++ - 48); /* 48 es el código ascii de '0' */
  6172.     else
  6173.       return (-1); /* carácter no válido */
  6174.  
  6175.   *valor *= signo;
  6176.  
  6177.   return (0);
  6178. }
  6179.  
  6180. /*
  6181.   Convierte un valor entero a su representación string.
  6182. */
  6183.  
  6184. void int_a_ascii (int valor, char *string)
  6185. {
  6186.   int signo = valor;
  6187.  
  6188.   char temporal, *str = string;
  6189.  
  6190.   if (valor < 0)
  6191.     valor *= -1;
  6192.  
  6193.   do
  6194.     {
  6195.       *string++ = (valor % 10) + 48; /* 48 es el código ascii de '0' */
  6196.       valor /= 10;
  6197.     } while (valor > 0);
  6198.  
  6199.   if (signo < 0)
  6200.     *string++ = '-';
  6201.  
  6202.   *string-- = '\0';
  6203.  
  6204.   while (str < string)
  6205.     {
  6206.       temporal = *string;
  6207.       *string-- = *str;
  6208.       *str++ = temporal;
  6209.     }
  6210. }
  6211.  
  6212. /*
  6213.   Devuelve una copia del string s.
  6214.   Si no se puede hacer la copia (no hay suficiente memoria) devuelve NULL.
  6215.   Esta memoria reservada tendrá que ser liberada posteriormente con la
  6216.   función free().
  6217. */
  6218.  
  6219. char *salvar_string (char *s)
  6220. {
  6221.   char *str;
  6222.  
  6223.   if ((str = (char *) calloc (longitud_string (s) + 1, sizeof (char))) != NULL)
  6224.     copiar_rapido_string (s, str);
  6225.  
  6226.   return (str);
  6227. }
  6228. ende
  6229. begine " ESCRITURA ALFABETICA DE NUMEROS "
  6230. /*
  6231.   Programa que lee un número en dígitos decimales y los transforma en
  6232.   caracteres alfabéticos, es decir, en texto.
  6233.  
  6234.   El código está bastante documentado, así que no es difícil comprender
  6235.   la lógica seguida.
  6236.  
  6237.   Sólo hay una novedad en este programa: la inclusión de la librería <string.h>
  6238.   con la utilización de dos funciones de ellas: strcpy() y strcat(). En la
  6239.   librería <string.h> tenemos un montón de funciones relacionadas con strings.
  6240.   La mayoría de las funciones implementadas en el ejemplo anterior están en
  6241.   esta librería. La función strcpy() copia un string en otro y la función
  6242.   strcat() añade un string a otro. Ambas funciones las implementamos en el
  6243.   ejemplo anterior. El prototipo de estas dos funciones en el fichero string.h
  6244.   es:
  6245.  
  6246.     char *strcpy (char *destino, const char *fuente); /* devuelve destino */
  6247.     char *strcat (char *destino, const char *fuente); /* devuelve destino */
  6248.  
  6249.   Como curiosidad diré que este es el segundo programa que hice en C y con
  6250.   el que me adentré en el mundo de este lenguaje. Nunca lo he visto hecho en
  6251.   ningún sitio (ni ejecutable ni fuente).
  6252. */
  6253.  
  6254. /* Ficheros a incluir: */
  6255.  
  6256. #include <stdio.h>  /* printf () */
  6257. #include <conio.h>  /* getch (), putch (), cgets () */
  6258. #include <string.h> /* strcpy (), strcat () */
  6259.  
  6260. /* Macros: */
  6261.  
  6262. #define IMPAR(x)                ((x) % 2)
  6263. #define CARACTER_A_MAYUSCULA(c) ((c) >= 'a' && (c) <= 'z' ? ((c) & ~32) : (c))
  6264. #define ES_DIGITO(caracter)     ((caracter) >= '0' && (caracter) <= '9')
  6265.  
  6266. #define BOOLEAN          int
  6267. #define TRUE             1
  6268. #define FALSE            0
  6269.  
  6270. #define NUMMAXDIGITOS    36
  6271. #define NUMMAXCARACTERES 500
  6272.  
  6273. #define MASC             'M'
  6274. #define FEM              'F'
  6275.  
  6276. #define ESC         27
  6277. #define ENTER            13
  6278.  
  6279. /* Las siguientes constantes están definidas como macros para facilitar
  6280.    posibles cambios sobre ellas como convertirlas en minúsculas */
  6281.  
  6282. #define PRESENTACION     "PROGRAMA: ESCRITURA ALFABETICA DE NUMEROS"
  6283. #define NADA             "NO SE HA INTRODUCIDO NINGUN NUMERO"
  6284. #define ERROR            "NUMERO DEMASIADO LARGO"
  6285. #define Y                "Y "
  6286. #define UNO              "UNO "
  6287. #define UN               "UN "
  6288. #define UNA              "UNA "
  6289. #define CERO             "CERO"
  6290. #define CIEN             "CIEN "
  6291. #define MIL              "MIL "
  6292. #define MILLON           "UN MILLON "
  6293. #define MILLONES         "MILLONES "
  6294. #define BILLON              "UN BILLON "
  6295. #define BILLONES         "BILLONES "
  6296. #define TRILLON          "UN TRILLON "
  6297. #define TRILLONES        "TRILLONES "
  6298. #define CUATRILLON       "UN CUATRILLON "
  6299. #define CUATRILLONES     "CUATRILLONES "
  6300. #define QUINTILLON       "UN QUINTILLON "
  6301. #define QUINTILLONES     "QUINTILLONES "
  6302.  
  6303. /* Tabla de los nombres de los 20 primeros números: */
  6304.  
  6305. char         *tabnom [] = { "", "", "DOS ", "TRES ", "CUATRO ", "CINCO ",
  6306.                       "SEIS ", "SIETE ", "OCHO ", "NUEVE ", "DIEZ ", "ONCE ",
  6307.                       "DOCE ", "TRECE ", "CATORCE ", "QUINCE ", "DIECISEIS ",
  6308.                       "DIECISIETE ", "DIECIOCHO ", "DIECINUEVE ", "VEINTE "};
  6309.  
  6310. /* Tabla de los nombres de las decenas: */
  6311.  
  6312. char         *tabdec [] = { "", "ANTONIO", "VEINTI", "TREINTA ", "CUARENTA ",
  6313.               "CINCUENTA ", "SESENTA ", "SETENTA ", "OCHENTA ", "NOVENTA " };
  6314.  
  6315. /* Tabla de los nombres de las centenas masculinas: */
  6316.  
  6317. char         *tabcenmasc [] = { "", "CIENTO ", "DOSCIENTOS ", "TRESCIENTOS ",
  6318.               "CUATROCIENTOS ", "QUINIENTOS ", "SEISCIENTOS ", "SETECIENTOS ",
  6319.               "OCHOCIENTOS ", "NOVECIENTOS " };
  6320.  
  6321. /* Tabla de los nombre de las centenas femeninas: */
  6322.  
  6323. char         *tabcenfem  [] = { "", "CIENTO ", "DOSCIENTAS ", "TRESCIENTAS ",
  6324.              "CUATROCIENTAS ", "QUINIENTAS ", "SEISCIENTAS ", "SETECIENTAS ",
  6325.              "OCHOCIENTAS ", "NOVECIENTAS " };
  6326.  
  6327. /* Declaración de las variables globales: */
  6328.  
  6329. int tabnum [NUMMAXDIGITOS];     /* se almacena el número leído        */
  6330. char nomnum [NUMMAXCARACTERES]; /* número en caracteres alfábeticos   */
  6331. int numdigitos;                 /* número de dígitos del número leído */
  6332.  
  6333. /* Declaración de las funciones utilizadas en el programa: */
  6334.  
  6335. int  leer_numero (void);
  6336. char leer_genero (void);
  6337. void obtener_nombre_de_numero (int *indice, char genero, int grupostrescifras);
  6338. int  numero (int ind, int ncifras);
  6339. void nombre_de_numero_de_dos_cifras (int ind, char gener);
  6340. void nombre_de_numero_de_tres_cifras (int *indic, char genero);
  6341. void nombre_de_numero_de_seis_cifras (int *indic, char genero);
  6342. void nombre_de_numero_de_x_cifras (int *indic, char gener, int numcifras,
  6343.                                    char *sustsingular, char *sustplural);
  6344.  
  6345. /* Definición de funciones: */
  6346.  
  6347. void main (void)
  6348. {
  6349.   int grupostrescifras, indice, genero;
  6350.   BOOLEAN seguir = TRUE;
  6351.  
  6352.   while (seguir)
  6353.     {
  6354.       /* inicializar variables: */
  6355.       grupostrescifras = indice = numdigitos = 0;
  6356.  
  6357.       /* inicializar cadena: */
  6358.       strcpy (nomnum, "");
  6359.  
  6360.       /* presentar mensaje: */
  6361.       printf ("%s\n\r", PRESENTACION);
  6362.  
  6363.       /* obtener número y si éste es disntinto de cero también leer género: */
  6364.       if ((grupostrescifras = leer_numero ()) != 0 && numero (indice, 3) != 0)
  6365.         genero = leer_genero ();
  6366.  
  6367.       /* convertir número a texto: */
  6368.       obtener_nombre_de_numero (&indice, genero, grupostrescifras);
  6369.  
  6370.       /* escribir datos de salida: */
  6371.       printf ("\n\nNombre: %s", nomnum);
  6372.  
  6373.       /* preguntar si se desea seguir */
  6374.       printf ("\n\nPulsa ESC para terminar o cualquier otra tecla para seguir ");
  6375.       if (getch () == ESC)
  6376.         seguir = FALSE;
  6377.       else
  6378.         printf ("\r                                                           \r");
  6379.     }
  6380. } /* main */
  6381.  
  6382. int leer_numero (void)
  6383. {
  6384.   char ch;
  6385.   int cociente, resto, incremento;
  6386.   register int i, j;
  6387.   char numero[NUMMAXDIGITOS+3], *p; /* +3: dos primeros elementos y último */
  6388.   BOOLEAN numero_correcto = FALSE;
  6389.  
  6390.   /* leer número */
  6391.   while (! numero_correcto)
  6392.     {
  6393.       printf ("\nIntroduce número: ");
  6394.       numero[0] = NUMMAXDIGITOS;
  6395.       cgets (numero);
  6396.       for (p = numero + 2, numdigitos = 0; *p && ES_DIGITO (*p); p++, numdigitos++)
  6397.         tabnum[numdigitos] = *p - '0';
  6398.       if (*p)
  6399.         printf ("\nNúmero incorrecto.\n");
  6400.       else
  6401.         numero_correcto = TRUE;
  6402.     }
  6403.  
  6404.   if (numdigitos == 0)
  6405.     return (0);
  6406.   else
  6407.     {
  6408.       /* quitar los ceros de la izquierda en la tabla de números */
  6409.       for (i = 0; i < numdigitos && tabnum[i] == 0; i++)
  6410.         ;
  6411.       if (i > 0)
  6412.         for (j = i; j < numdigitos; j++)
  6413.           tabnum [j-i] = tabnum [j];
  6414.  
  6415.       /* hallar cociente y resto para averiguar los ceros que hacen falta
  6416.          añadir a la izquierda y devolver el número de grupos de tres cifras */
  6417.       switch (numdigitos -= i)
  6418.         {
  6419.           case 0 :
  6420.           case 1 : cociente = 0;
  6421.                    resto    = 1;
  6422.                    break;
  6423.           case 2 : cociente = 0;
  6424.                    resto    = 2;
  6425.                    break;
  6426.           default: cociente = numdigitos / 3;
  6427.                    resto    = numdigitos % 3;
  6428.         }
  6429.  
  6430.      /* Se añaden los ceros a la izquierda necesarios para completar los grupos
  6431.         de tres cifras o sextetos. */
  6432.  
  6433.       /* a la variable numdigitos se le aumenta incremento que es el numero de
  6434.       ceros que hay que añadir a la izquierda cuyo valor depende del resto*/
  6435.       numdigitos += (incremento = resto == 0 ? 0 : resto == 1 ? 2 : 1);
  6436.       if (numdigitos != 3 && IMPAR (numdigitos))
  6437.         incremento += 3; /* completa sexteto (grupo de seis cifras) */
  6438.  
  6439.       /* introducir en la tabla de los números los ceros a la izquierda
  6440.          necesarios para completar el trío o sexteto */
  6441.        for (i = numdigitos - 1; i >= 0; i--)
  6442.          tabnum [i+incremento] = tabnum [i];
  6443.        for (i = 0; i < incremento; i++)
  6444.          tabnum [i] = 0;
  6445.  
  6446.        /* devolver el numero de grupos de tres cifras */
  6447.        return (resto != 0 ? cociente + 1 : cociente);
  6448.     }
  6449. } /* leernumero */
  6450.  
  6451. char leer_genero (void)
  6452. {
  6453.   char ch;
  6454.  
  6455.   printf ("\n\nGénero (M o ENTER: masculino, F: femenino): ");
  6456.   do
  6457.     {
  6458.       ch = getch ();
  6459.     } while (CARACTER_A_MAYUSCULA (ch) != 'M' &&
  6460.              CARACTER_A_MAYUSCULA (ch) != 'F' && ch != ENTER);
  6461.  
  6462.   if (ch == ENTER)
  6463.     ch = 'M';
  6464.   else
  6465.     ch = CARACTER_A_MAYUSCULA (ch);
  6466.  
  6467.   return (putch (ch)); /* escribe carácter y lo devuelve */
  6468. } /* leergenero */
  6469.  
  6470. void obtener_nombre_de_numero (int *indice, char genero, int grupostrescifras)
  6471. {
  6472.   switch (grupostrescifras)
  6473.     {
  6474.       case 0 :
  6475.         strcpy (nomnum, NADA);
  6476.         break;
  6477.       case 1 :
  6478.         if (*indice == 0 && numero (*indice, 3) ==0)
  6479.           strcpy (nomnum, CERO);
  6480.         else
  6481.           nombre_de_numero_de_tres_cifras (indice, genero);
  6482.         break;
  6483.       case 2 :
  6484.         nombre_de_numero_de_seis_cifras (indice, genero);
  6485.         break;
  6486.       case 3 :
  6487.       case 4 :
  6488.         nombre_de_numero_de_x_cifras (indice, genero, 12, MILLON, MILLONES);
  6489.         break;
  6490.       case 5 :
  6491.       case 6 :
  6492.         nombre_de_numero_de_x_cifras (indice, genero, 18, BILLON, BILLONES);
  6493.         break;
  6494.       case 7 :
  6495.       case 8 :
  6496.         nombre_de_numero_de_x_cifras (indice, genero, 24, TRILLON, TRILLONES);
  6497.         break;
  6498.       case 9 :
  6499.       case 10:
  6500.         nombre_de_numero_de_x_cifras (indice, genero, 30, CUATRILLON, CUATRILLONES);
  6501.         break;
  6502.       case 11:
  6503.       case 12:
  6504.         nombre_de_numero_de_x_cifras (indice, genero, 36, QUINTILLON, QUINTILLONES);
  6505.         break;
  6506.       default:
  6507.         strcpy (nomnum, ERROR);
  6508.     }
  6509. } /* obtener_nombre_de_numero */
  6510.  
  6511. /*
  6512.   Esta función convierte unas determinadas cifras de tabnum en un número.
  6513.   Concretamente convierte las cifras de tabnum que empiezan en el índice
  6514.   ind y terminan en ind+ncifras-1.
  6515. */
  6516.  
  6517. int numero (int ind, int ncifras)
  6518. {
  6519.   register int i, num;
  6520.  
  6521.   for (i = num = 0; i < ncifras; i++)
  6522.     num = 10 * num + tabnum [ind + i];
  6523.  
  6524.   return (num);
  6525. } /* numero */
  6526.  
  6527. /*
  6528.   Esta función convierte un número de dos cifras en texto
  6529. */
  6530.  
  6531. void nombre_de_numero_de_dos_cifras (int ind, char gener)
  6532. {
  6533.   /* asigna valor al elemento de índice 1 de la tabla tabnom */
  6534.   tabnom [1] = gener == FEM ? UNA : (numdigitos - ind <= 2 ? UNO : UN);
  6535.  
  6536.   /* si número a escribir es menor que 20, leemos nombre de tabnom */
  6537.   if (numero (ind, 2) <= 20)
  6538.     strcat (nomnum, tabnom [numero (ind, 2)]);
  6539.  
  6540.   /* número mayor que 20 */
  6541.   else
  6542.     {
  6543.       /* añadimos nombre de la decena */
  6544.       strcat (nomnum, tabdec [numero (ind, 1)]);
  6545.  
  6546.       /* si no es decena de veintena y no termina en 0, añadimos Y */
  6547.       if (numero (ind + 1, 1) != 0 && numero (ind, 1) != 2)
  6548.         strcat (nomnum, Y);
  6549.  
  6550.       /* añadimos nombre de la unidad */
  6551.       strcat (nomnum, tabnom [numero (ind + 1, 1)]);
  6552.     }
  6553. } /* nombre_de_numero_de_dos_cifras */
  6554.  
  6555. /*
  6556.   Esta función convierte un número de tres cifras en texto
  6557. */
  6558.  
  6559. void nombre_de_numero_de_tres_cifras (int *indic, char genero)
  6560. {
  6561.   /* si número de tres cifras es 0, no se hace nada */
  6562.   if (numero (*indic, 3) == 0)
  6563.     ;
  6564.  
  6565.   /* si número de tres cifras en 100, se añade CIEN */
  6566.   else if (numero (*indic, 3) == 100)
  6567.     strcat (nomnum, CIEN);
  6568.  
  6569.   /* si no es ni 0 ni 100 */
  6570.   else
  6571.     {
  6572.       /* añadimos nombre de centena */
  6573.       strcat (nomnum, genero == MASC ?
  6574.               tabcenmasc [numero (*indic, 1)] : tabcenfem  [numero (*indic, 1)]);
  6575.  
  6576.       /* añadimos nombre del grupo de dos cifras restantes */
  6577.       nombre_de_numero_de_dos_cifras (*indic + 1, genero);
  6578.     }
  6579.  
  6580.   /* aumentamos índice en 3 ya que hemos procesado tres cifras de tabnum */
  6581.   *indic += 3;
  6582. } /* nombre_de_numero_de_tres_cifras */
  6583.  
  6584. /*
  6585.   Esta función convierte un número de seis cifras en texto
  6586. */
  6587.  
  6588. void nombre_de_numero_de_seis_cifras (int *indic, char genero)
  6589. {
  6590.   /* si primeras tres cifras en 0, se procesan las tres últimas cifras */
  6591.   if (numero (*indic, 3) == 0)
  6592.     *indic += 3;
  6593.  
  6594.   /* procesar tres primeras cifras del grupo de seis */
  6595.   else
  6596.     {
  6597.       /* si valor de tres primeras cifras es 1, no se escribe nada */
  6598.       if (numero (*indic, 3) == 1)
  6599.         *indic += 3;
  6600.  
  6601.       /* escribir nombre correspondiente a las tres primeras cifras */
  6602.       else
  6603.         nombre_de_numero_de_tres_cifras (indic, genero);
  6604.  
  6605.       /* añadir MIL al nombre de las tres primeras cifras si eran distintas de 1 */
  6606.       strcat (nomnum, MIL);
  6607.     }
  6608.  
  6609.   /* escribir nombre de grupo de tres cifras */
  6610.   nombre_de_numero_de_tres_cifras (indic, genero);
  6611. } /* nombre_de_numero_de_seis_cifras */
  6612.  
  6613. /*
  6614.   Esta función convierte un número de x cifras en texto.
  6615.   x es mayor de 6.
  6616. */
  6617.  
  6618. void nombre_de_numero_de_x_cifras (int *indic, char gener, int numcifras,
  6619.                                    char *sustsingular, char *sustplural)
  6620. {
  6621.   /* si valor de número a escribir es 0, no se escribe nada */
  6622.   if (numero (*indic, numcifras) == 0)
  6623.     *indic += numcifras;
  6624.  
  6625.   /* si valor de número a escribir es distinto de 0 */
  6626.   else
  6627.     {
  6628.       /* si valor de las 6 primeras cifras es 0, no se escribe nada */
  6629.       if (numero (*indic, 6) == 0)
  6630.         *indic += 6;
  6631.  
  6632.       /* si valor de las 6 primeras cifras es 1, escribir sustantivo en
  6633.          singular; por ejemplo, si trabajamos con trillones, se escribe
  6634.          TRILLON no TRILLONES */
  6635.       else if (numero (*indic, 6) == 1)
  6636.         {
  6637.           *indic += 6;
  6638.           strcat (nomnum, sustsingular);
  6639.         }
  6640.  
  6641.       /* si valor de las 6 primeras cifras es distinto de 0, llama a
  6642.          función obtener_nombre_de_numero para que escriba las 6 primeras
  6643.          cifras, que han de escribirse en masculino puesto que el género
  6644.          sólo es aplicable a las últimas cifras del número; una vez escritas
  6645.          las 6 primeras cifras se le añade el sustantivo plural, por ejemplo
  6646.          si trabajamos con trillones, se añadiría TRILLONES */
  6647.       else
  6648.         {
  6649.           obtener_nombre_de_numero (indic, MASC, 2);
  6650.           strcat (nomnum, sustplural);
  6651.         }
  6652.  
  6653.       /* una vez escritas las 6 primeras cifras junto con el sustantivo
  6654.          correspondiente, se escriben el resto de las cifras llamando a
  6655.          la función obtener_nombre_de_numero */
  6656.       obtener_nombre_de_numero (indic, gener, (numcifras -= 6) / 3);
  6657.     }
  6658. } /* nombre_de_numero_de_x_cifras */
  6659. ende
  6660. begine " ESCRITURA ALFABETICA DE NUMEROS VERSION 2 "
  6661. /*
  6662.   Programa que lee un número en dígitos decimales y los transforma en
  6663.   caracteres alfabéticos, es decir, en texto. Este programa es una versión
  6664.   mejorada del programa del ejemplo anterior.
  6665.  
  6666.   Se han hecho dos mejoras importantes:
  6667.  
  6668.     1) El programa del ejemplo anterior sólo aceptaba números enteros. Este
  6669.       programa acepta también números reales. La sintaxis de un número real
  6670.       correcta es la sintaxis de un número double en C. Esta sintaxis se
  6671.       describe unas líneas más abajo al explicar la función strtod().
  6672.  
  6673.     2) En el programa anterior las palabras correspondientes al número en
  6674.       versión alfabética se partían en las líneas, lo cual no es muy estético.
  6675.       Esta versión permite escribir las palabras del nombre del número entre
  6676.       dos columnas especificadas y son partir las palabras entre líneas.
  6677.  
  6678.   El código está bastante documentado, así que no es difícil comprender
  6679.   la lógica seguida.
  6680.  
  6681.   Se han utilizado dos nuevas funciones con respecto al de la primera
  6682.   versión:
  6683.  
  6684.     1) char *strchr (const char *s, int c);
  6685.  
  6686.       El protipo anterior se encuentra en el fichero string.h.
  6687.  
  6688.       Esta función devuelve un puntero a la primera ocurrencia del carácter
  6689.       c en s; si c no aparece en s, strchr devuelve NULL.
  6690.  
  6691.     2) double strtod (const char *s, char **endptr);
  6692.  
  6693.       El prototipo anterior se encuentra en el fichero stdlib.h
  6694.  
  6695.       Esta función devuelve el valor de como un double, donde s es una
  6696.       secuencia de caracteres. El string debe tener el siguiente formato:
  6697.  
  6698.          [eb] [sn] [ddd] [.] [ddd] [fmt[sn]ddd]
  6699.                                  ══╗
  6700.          eb   =  espacio blanco    ║
  6701.          sn   =  signo (+ o -)     ║   Todos los elementos
  6702.          ddd  =  dígitos           ╟─        entre [ ]
  6703.          fmt  =  e o E             ║      son opcionales
  6704.           .   =  punto decimal     ║
  6705.                                  ══╝
  6706.      El puntero endptr apunta, al terminar la función strtod, al último
  6707.      carácter válido: si el string tiene la sintaxis correcta de un número
  6708.      double, apuntará al carácter terminador nulo, en caso contrario apuntará
  6709.      al primer carácter de s que no coincida con la sintaxis de un double.
  6710. */
  6711.  
  6712. /* Ficheros a incluir: */
  6713.  
  6714. #include <stdio.h>  /* printf () */
  6715. #include <conio.h>  /* getch (), cgets () */
  6716. #include <string.h> /* strcpy (), strcat (), strchr () */
  6717. #include <stdlib.h> /* strtod () */
  6718.  
  6719. /* Macros: */
  6720.  
  6721. #define NUM(x)                  ((x) - '0')
  6722. #define IMPAR(x)                ((x) % 2)
  6723. #define CARACTER_A_MAYUSCULA(c) ((c) >= 'a' && (c) <= 'z' ? ((c) & ~32) : (c))
  6724. #define ES_DIGITO(caracter)     ((caracter) >= '0' && (caracter) <= '9')
  6725. #define ENTRE(x,x1,x2)          ((x) >= (x1) && (x) <= (x2))
  6726.  
  6727. #define BOOLEAN          int
  6728. #define TRUE             1
  6729. #define FALSE            0
  6730.  
  6731. #define NUMMAXDIGITOS    36
  6732. #define NUMMAXCARACTERES 500
  6733.  
  6734. #define MASC             'M'
  6735. #define FEM              'F'
  6736.  
  6737. #define ESC         27
  6738. #define ENTER            13
  6739. #define ESPACIO          32
  6740.  
  6741. #define COLUMNA_MINIMA   2
  6742. #define COLUMNA_MAXIMA   79
  6743. #define DIFERENCIA_MINIMA_ENTRE_COLUMNAS 10
  6744.  
  6745. /* Las siguientes constantes están definidas como macros para facilitar
  6746.    posibles cambios sobre ellas como convertirlas en minúsculas */
  6747.  
  6748. #define PRESENTACION_VERSION_2 "PROGRAMA: ESCRITURA ALFABETICA DE NUMEROS"
  6749. #define NADA             "NO SE HA INTRODUCIDO NINGUN NUMERO"
  6750. #define ERROR            "NUMERO DEMASIADO LARGO"
  6751. #define Y                "Y "
  6752. #define COMA             "COMA "
  6753. #define POTENCIA         "POR DIEZ ELEVADO A "
  6754. #define MAS              "MAS "
  6755. #define MENOS            "MENOS "
  6756. #define UNO              "UNO "
  6757. #define UN               "UN "
  6758. #define UNA              "UNA "
  6759. #define CERO             "CERO "
  6760. #define CIEN             "CIEN "
  6761. #define MIL              "MIL "
  6762. #define MILLON           "UN MILLON "
  6763. #define MILLONES         "MILLONES "
  6764. #define BILLON             "UN BILLON "
  6765. #define BILLONES         "BILLONES "
  6766. #define TRILLON          "UN TRILLON "
  6767. #define TRILLONES        "TRILLONES "
  6768. #define CUATRILLON       "UN CUATRILLON "
  6769. #define CUATRILLONES     "CUATRILLONES "
  6770. #define QUINTILLON       "UN QUINTILLON "
  6771. #define QUINTILLONES     "QUINTILLONES "
  6772.  
  6773. /* Tabla de los nombres de los 20 primeros números: */
  6774.  
  6775. char         *tabnom [] = { "", "", "DOS ", "TRES ", "CUATRO ", "CINCO ",
  6776.                       "SEIS ", "SIETE ", "OCHO ", "NUEVE ", "DIEZ ", "ONCE ",
  6777.                       "DOCE ", "TRECE ", "CATORCE ", "QUINCE ", "DIECISEIS ",
  6778.                       "DIECISIETE ", "DIECIOCHO ", "DIECINUEVE ", "VEINTE "};
  6779.  
  6780. /* Tabla de los nombres de las decenas: */
  6781.  
  6782. char         *tabdec [] = { "", "ANTONIO", "VEINTI", "TREINTA ", "CUARENTA ",
  6783.               "CINCUENTA ", "SESENTA ", "SETENTA ", "OCHENTA ", "NOVENTA " };
  6784.  
  6785. /* Tabla de los nombres de las centenas masculinas: */
  6786.  
  6787. char         *tabcenmasc [] = { "", "CIENTO ", "DOSCIENTOS ", "TRESCIENTOS ",
  6788.              "CUATROCIENTOS ", "QUINIENTOS ", "SEISCIENTOS ", "SETECIENTOS ",
  6789.              "OCHOCIENTOS ", "NOVECIENTOS " };
  6790.  
  6791. /* Tabla de los nombre de las centenas femeninas: */
  6792.  
  6793. char         *tabcenfem  [] = { "", "CIENTO ", "DOSCIENTAS ", "TRESCIENTAS ",
  6794.              "CUATROCIENTAS ", "QUINIENTAS ", "SEISCIENTAS ", "SETECIENTAS ",
  6795.              "OCHOCIENTAS ", "NOVECIENTAS " };
  6796.  
  6797. /* Declaración de las variables globales: */
  6798.  
  6799. int tabnum [NUMMAXDIGITOS];     /* se almacena el número leído        */
  6800. char nomnum [NUMMAXCARACTERES]; /* número en caracteres alfábeticos   */
  6801. int numdigitos;                 /* número de dígitos del número leído */
  6802.  
  6803. /* Declaración de las funciones utilizadas en el programa: */
  6804.  
  6805. void leer_numero (char numero[]);
  6806. char leer_genero (void);
  6807. void leer_columnas (int *columna1, int *columna2);
  6808. void obtener_numero (char *strnum, char gen);
  6809. void obtener_num_sin_exp (char *s, char gen);
  6810. void obtener_parte_entera_num (char *s, char gen);
  6811. void obtener_nombre_de_numero (int *indice, char genero, int grupostrescifras);
  6812. int  numero (int ind, int ncifras);
  6813. void nombre_de_numero_de_dos_cifras (int ind, char gener);
  6814. void nombre_de_numero_de_tres_cifras (int *indic, char genero);
  6815. void nombre_de_numero_de_seis_cifras (int *indic, char genero);
  6816. void nombre_de_numero_de_x_cifras (int *indic, char gener, int numcifras,
  6817.                                    char *sustsingular, char *sustplural);
  6818. void escribir (char *s, int c1, int c2);
  6819.  
  6820. /* Definición de funciones: */
  6821.  
  6822. void main (void)
  6823. {
  6824.   char genero;
  6825.   BOOLEAN seguir = TRUE;
  6826.   char numero[NUMMAXDIGITOS+3];
  6827.   int c1, c2;
  6828.  
  6829.   while (seguir)
  6830.     {
  6831.       /* presentar mensaje: */
  6832.       printf ("%s\n\r", PRESENTACION_VERSION_2);
  6833.  
  6834.       /* leer número, género y columnas: */
  6835.       leer_numero (numero);
  6836.       genero = leer_genero ();
  6837.       leer_columnas (&c1, &c2);
  6838.  
  6839.       /* convertir número a texto: */
  6840.       obtener_numero (numero+2, genero);
  6841.  
  6842.       /* escribir datos de salida: */
  6843.       printf ("\nNombre correspondiente al número introducido:\n\n");
  6844.       escribir (nomnum, c1, c2);
  6845.  
  6846.       /* preguntar si se desea seguir */
  6847.       printf ("\n\nPulsa ESC para terminar o cualquier otra tecla para seguir ");
  6848.       if (getch () == ESC)
  6849.         seguir = FALSE;
  6850.       else
  6851.         printf ("\r                                                           \r");
  6852.     }
  6853. } /* main */
  6854.  
  6855. void leer_numero (char numero[])
  6856. {
  6857.   char *puntero_al_final;
  6858.   BOOLEAN numero_correcto = FALSE;
  6859.  
  6860.   while (! numero_correcto)
  6861.     {
  6862.       printf ("\nIntroduce número: ");
  6863.       numero[0] = NUMMAXDIGITOS;
  6864.       cgets (numero);
  6865.       strtod (numero + 2, &puntero_al_final);
  6866.       if (*puntero_al_final)
  6867.         printf ("\nNúmero incorrecto.\n");
  6868.       else
  6869.         numero_correcto = TRUE;
  6870.     }
  6871. } /* leernumero */
  6872.  
  6873. char leer_genero (void)
  6874. {
  6875.   char ch;
  6876.  
  6877.   printf ("\n\nGénero (M o ENTER: masculino, F: femenino): ");
  6878.   do
  6879.     {
  6880.       ch = getch ();
  6881.     } while (CARACTER_A_MAYUSCULA (ch) != 'M' &&
  6882.              CARACTER_A_MAYUSCULA (ch) != 'F' && ch != ENTER);
  6883.  
  6884.   if (ch == ENTER)
  6885.     ch = 'M';
  6886.   else
  6887.     ch = CARACTER_A_MAYUSCULA (ch);
  6888.  
  6889.   printf ("%c\n", ch);
  6890.  
  6891.   return (ch);
  6892. } /* leergenero */
  6893.  
  6894. void leer_columnas (int *columna1, int *columna2)
  6895. {
  6896.   BOOLEAN columnas_correctas = FALSE;
  6897.  
  6898.   while (! columnas_correctas)
  6899.     {
  6900.       printf ("\nIntroduce las dos columnas (entre %d y %d con diferencia mínima %d): ",
  6901.               COLUMNA_MINIMA, COLUMNA_MAXIMA, DIFERENCIA_MINIMA_ENTRE_COLUMNAS);
  6902.       scanf ("%d%d", columna1, columna2);
  6903.       if (ENTRE (*columna1, COLUMNA_MINIMA, COLUMNA_MAXIMA) &&
  6904.           ENTRE (*columna2, COLUMNA_MINIMA, COLUMNA_MAXIMA) &&
  6905.           *columna2 - *columna1 + 1 >= DIFERENCIA_MINIMA_ENTRE_COLUMNAS)
  6906.         columnas_correctas = TRUE;
  6907.       else
  6908.         printf ("\nLos números de columnas introducidos son incorrectos.\n");
  6909.     }
  6910. } /* leer_columnas */
  6911.  
  6912. void obtener_numero (char *strnum, char gen)
  6913. {
  6914.   char *sb, *se; /* string base y string exponente */
  6915.  
  6916.   strcpy (nomnum, "");
  6917.   /* dividir strnum en base y exponente si existe exponente */
  6918.   sb = strnum;
  6919.   se = strchr (sb, 'e');
  6920.   if (se == NULL)
  6921.     se = strchr (sb, 'E');
  6922.   if (se == NULL)
  6923.     obtener_num_sin_exp (sb, gen);
  6924.   else
  6925.     {
  6926.       *se++ = 0; /* divide el strings se en dos substrings: uno con la base y otro con el exponente */
  6927.       obtener_num_sin_exp (sb, gen);
  6928.       strcat (nomnum, POTENCIA);
  6929.       obtener_num_sin_exp (se, gen);
  6930.     }
  6931. } /* obtener_numero */
  6932.  
  6933. /*
  6934.   Obtiene el número correspondiente a un string sin exponente.
  6935. */
  6936.  
  6937. void obtener_num_sin_exp (char *s, char gen)
  6938. {
  6939.   register char *si, *sr; /* string parte integer y string parte real */
  6940.  
  6941.   si = s;
  6942.   sr = strchr (s, '.');
  6943.   if (sr == NULL)
  6944.     obtener_parte_entera_num (s, gen);
  6945.   else
  6946.     {
  6947.       *sr++ = 0; /* divide el string sr en dos substrings: uno con parte entera y otro con parte real */
  6948.       obtener_parte_entera_num (si, gen);
  6949.       strcat (nomnum, COMA);
  6950.       obtener_parte_entera_num (sr, gen);
  6951.     }
  6952. } /* obtener_num_sin_exp */
  6953.  
  6954. /*
  6955.   Obtiene el número correspondiente a un string sin parte real
  6956. */
  6957.  
  6958. void obtener_parte_entera_num (char *s, char gen)
  6959. {
  6960.   int grupos_tres_cifras, numdigitos, indice = 0;
  6961.   register int i, j;
  6962.   int cociente, resto, incremento;
  6963.  
  6964.   /* comprobar si hay signo */
  6965.   if (*s == '+' || *s == '-')
  6966.     strcat (nomnum, *s++ == '+' ? MAS : MENOS );
  6967.  
  6968.   for (numdigitos = 0; *s; numdigitos++, s++)
  6969.     tabnum[numdigitos] = NUM (*s);
  6970.  
  6971.   if (numdigitos == 0)
  6972.     grupos_tres_cifras = 0;
  6973.   else
  6974.     {
  6975.       /* quitar los ceros de la izquierda en la tabla de números */
  6976.       for (i = 0; i < numdigitos && tabnum[i] == 0; i++)
  6977.     ;
  6978.       if (i > 0)
  6979.         for (j = i; j < numdigitos; j++)
  6980.           tabnum [j-i] = tabnum [j];
  6981.  
  6982.       /* hallar cociente y resto para averiguar los ceros que hacen falta
  6983.          añadir a la izquierda y devolver el número de grupos de tres cifras */
  6984.       switch (numdigitos -= i)
  6985.         {
  6986.           case 0 :
  6987.           case 1 : cociente = 0;
  6988.                    resto    = 1;
  6989.                    break;
  6990.           case 2 : cociente = 0;
  6991.                    resto    = 2;
  6992.                    break;
  6993.           default: cociente = numdigitos / 3;
  6994.                    resto    = numdigitos % 3;
  6995.     }
  6996.  
  6997.      /* Se añaden los ceros a la izquierda necesarios para completar los grupos
  6998.        de tres cifras o sextetos. */
  6999.  
  7000.       /* a la variable numdigitos se le aumenta incremento que es el numero de
  7001.          ceros que hay que añadir a la izquierda cuyo valor depende del resto*/
  7002.       numdigitos += (incremento = resto == 0 ? 0 : resto == 1 ? 2 : 1);
  7003.       if (numdigitos != 3 && IMPAR (numdigitos))
  7004.         incremento += 3; /* completa sexteto (grupo de seis cifras) */
  7005.  
  7006.       /* introducir en la tabla de los números los ceros a la izquierda
  7007.        necesarios para completar el trío o sexteto */
  7008.        for (i = numdigitos - 1; i >= 0; i--)
  7009.          tabnum [i+incremento] = tabnum [i];
  7010.        for (i = 0; i < incremento; i++)
  7011.          tabnum [i] = 0;
  7012.  
  7013.        /* devolver el numero de grupos de tres cifras */
  7014.        grupos_tres_cifras = resto != 0 ? cociente + 1 : cociente;
  7015.     }
  7016.  
  7017.   obtener_nombre_de_numero (&indice, gen, grupos_tres_cifras);
  7018. } /* obtener_parte_entera_num */
  7019.  
  7020. void obtener_nombre_de_numero (int *indice, char genero, int grupostrescifras)
  7021. {
  7022.   switch (grupostrescifras)
  7023.     {
  7024.       case 0 :
  7025.         strcat (nomnum, NADA);
  7026.         break;
  7027.       case 1 :
  7028.         if (*indice == 0 && numero (*indice, 3) ==0)
  7029.           strcat (nomnum, CERO);
  7030.         else
  7031.           nombre_de_numero_de_tres_cifras (indice, genero);
  7032.         break;
  7033.       case 2 :
  7034.         nombre_de_numero_de_seis_cifras (indice, genero);
  7035.         break;
  7036.       case 3 :
  7037.       case 4 :
  7038.         nombre_de_numero_de_x_cifras (indice, genero, 12, MILLON, MILLONES);
  7039.         break;
  7040.       case 5 :
  7041.       case 6 :
  7042.         nombre_de_numero_de_x_cifras (indice, genero, 18, BILLON, BILLONES);
  7043.         break;
  7044.       case 7 :
  7045.       case 8 :
  7046.         nombre_de_numero_de_x_cifras (indice, genero, 24, TRILLON, TRILLONES);
  7047.         break;
  7048.       case 9 :
  7049.       case 10:
  7050.         nombre_de_numero_de_x_cifras (indice, genero, 30, CUATRILLON, CUATRILLONES);
  7051.         break;
  7052.       case 11:
  7053.       case 12:
  7054.         nombre_de_numero_de_x_cifras (indice, genero, 36, QUINTILLON, QUINTILLONES);
  7055.         break;
  7056.       default:
  7057.         strcpy (nomnum, ERROR);
  7058.     }
  7059. } /* obtener_nombre_de_numero */
  7060.  
  7061. /*
  7062.   Esta función convierte unas determinadas cifras de tabnum en un número.
  7063.   Concretamente convierte las cifras de tabnum que empiezan en el índice
  7064.   ind y terminan en ind+ncifras-1.
  7065. */
  7066.  
  7067. int numero (int ind, int ncifras)
  7068. {
  7069.   register int i, num;
  7070.  
  7071.   for (i = num = 0; i < ncifras; i++)
  7072.     num = 10 * num + tabnum [ind + i];
  7073.  
  7074.   return (num);
  7075. } /* numero */
  7076.  
  7077. /*
  7078.   Esta función convierte un número de dos cifras en texto
  7079. */
  7080.  
  7081. void nombre_de_numero_de_dos_cifras (int ind, char gener)
  7082. {
  7083.   /* asigna valor al elemento de índice 1 de la tabla tabnom */
  7084.   tabnom [1] = gener == FEM ? UNA : (numdigitos - ind <= 2 ? UNO : UN);
  7085.  
  7086.   /* si número a escribir es menor que 20, leemos nombre de tabnom */
  7087.   if (numero (ind, 2) <= 20)
  7088.     strcat (nomnum, tabnom [numero (ind, 2)]);
  7089.  
  7090.   /* número mayor que 20 */
  7091.   else
  7092.     {
  7093.       /* añadimos nombre de la decena */
  7094.       strcat (nomnum, tabdec [numero (ind, 1)]);
  7095.  
  7096.       /* si no es decena de veintena y no termina en 0, añadimos Y */
  7097.       if (numero (ind + 1, 1) != 0 && numero (ind, 1) != 2)
  7098.         strcat (nomnum, Y);
  7099.  
  7100.       /* añadimos nombre de la unidad */
  7101.       strcat (nomnum, tabnom [numero (ind + 1, 1)]);
  7102.     }
  7103. } /* nombre_de_numero_de_dos_cifras */
  7104.  
  7105. /*
  7106.   Esta función convierte un número de tres cifras en texto
  7107. */
  7108.  
  7109. void nombre_de_numero_de_tres_cifras (int *indic, char genero)
  7110. {
  7111.   /* si número de tres cifras es 0, no se hace nada */
  7112.   if (numero (*indic, 3) == 0)
  7113.     ;
  7114.  
  7115.   /* si número de tres cifras en 100, se añade CIEN */
  7116.   else if (numero (*indic, 3) == 100)
  7117.     strcat (nomnum, CIEN);
  7118.  
  7119.   /* si no es ni 0 ni 100 */
  7120.   else
  7121.     {
  7122.       /* añadimos nombre de centena */
  7123.       strcat (nomnum, genero == MASC ?
  7124.               tabcenmasc [numero (*indic, 1)] : tabcenfem  [numero (*indic, 1)]);
  7125.  
  7126.       /* añadimos nombre del grupo de dos cifras restantes */
  7127.       nombre_de_numero_de_dos_cifras (*indic + 1, genero);
  7128.     }
  7129.  
  7130.   /* aumentamos índice en 3 ya que hemos procesado tres cifras de tabnum */
  7131.   *indic += 3;
  7132. } /* nombre_de_numero_de_tres_cifras */
  7133.  
  7134. /*
  7135.   Esta función convierte un número de seis cifras en texto
  7136. */
  7137.  
  7138. void nombre_de_numero_de_seis_cifras (int *indic, char genero)
  7139. {
  7140.   /* si primeras tres cifras en 0, se procesan las tres últimas cifras */
  7141.   if (numero (*indic, 3) == 0)
  7142.     *indic += 3;
  7143.  
  7144.   /* procesar tres primeras cifras del grupo de seis */
  7145.   else
  7146.     {
  7147.       /* si valor de tres primeras cifras es 1, no se escribe nada */
  7148.       if (numero (*indic, 3) == 1)
  7149.         *indic += 3;
  7150.  
  7151.       /* escribir nombre correspondiente a las tres primeras cifras */
  7152.       else
  7153.         nombre_de_numero_de_tres_cifras (indic, genero);
  7154.  
  7155.       /* añadir MIL al nombre de las tres primeras cifras si eran distintas de 1 */
  7156.       strcat (nomnum, MIL);
  7157.     }
  7158.  
  7159.   /* escribir nombre de grupo de tres cifras */
  7160.   nombre_de_numero_de_tres_cifras (indic, genero);
  7161. } /* nombre_de_numero_de_seis_cifras */
  7162.  
  7163. /*
  7164.   Esta función convierte un número de x cifras en texto.
  7165.   x es mayor de 6.
  7166. */
  7167.  
  7168. void nombre_de_numero_de_x_cifras (int *indic, char gener, int numcifras,
  7169.                                    char *sustsingular, char *sustplural)
  7170. {
  7171.   /* si valor de número a escribir es 0, no se escribe nada */
  7172.   if (numero (*indic, numcifras) == 0)
  7173.     *indic += numcifras;
  7174.  
  7175.   /* si valor de número a escribir es distinto de 0 */
  7176.   else
  7177.     {
  7178.       /* si valor de las 6 primeras cifras es 0, no se escribe nada */
  7179.       if (numero (*indic, 6) == 0)
  7180.         *indic += 6;
  7181.  
  7182.       /* si valor de las 6 primeras cifras es 1, escribir sustantivo en
  7183.          singular; por ejemplo, si trabajamos con trillones, se escribe
  7184.          TRILLON no TRILLONES */
  7185.       else if (numero (*indic, 6) == 1)
  7186.         {
  7187.           *indic += 6;
  7188.           strcat (nomnum, sustsingular);
  7189.         }
  7190.  
  7191.       /* si valor de las 6 primeras cifras es distinto de 0, llama a
  7192.          función obtener_nombre_de_numero para que escriba las 6 primeras
  7193.          cifras, que han de escribirse en masculino puesto que el género
  7194.          sólo es aplicable a las últimas cifras del número; una vez escritas
  7195.          las 6 primeras cifras se le añade el sustantivo plural, por ejemplo
  7196.          si trabajamos con trillones, se añadiría TRILLONES */
  7197.       else
  7198.         {
  7199.           obtener_nombre_de_numero (indic, MASC, 2);
  7200.           strcat (nomnum, sustplural);
  7201.         }
  7202.  
  7203.       /* una vez escritas las 6 primeras cifras junto con el sustantivo
  7204.          correspondiente, se escriben el resto de las cifras llamando a
  7205.          la función obtener_nombre_de_numero */
  7206.       obtener_nombre_de_numero (indic, gener, (numcifras -= 6) / 3);
  7207.     }
  7208. } /* nombre_de_numero_de_x_cifras */
  7209.  
  7210. /*
  7211.   Escribe un string entre las columnas c1 y c2 sin dividir las palabras
  7212.   del string en más de una línea.
  7213. */
  7214.  
  7215. void escribir (char *s, int c1, int c2)
  7216. {
  7217.   int unsigned l;  /* longitud */
  7218.   char ca = c1, /* columna actual */
  7219.        *st = s, /* s temporal, se mueve a través de s */
  7220.        *pal; /* palabra dentro de s */
  7221.  
  7222.   while (*s)
  7223.     {
  7224.       while (*s == ESPACIO)
  7225.         s++;
  7226.       if ((st = strchr (s, ESPACIO)) != NULL)
  7227.         {
  7228.           *st++ = 0;
  7229.           pal = s;
  7230.           s = st;
  7231.         }
  7232.       else
  7233.         {
  7234.           pal = s;
  7235.           *s = 0;
  7236.         }
  7237.       l = strlen (pal);
  7238.       if (l)
  7239.         if (ca == c1)
  7240.           printf ("%*c%s", c1-1, ESPACIO, pal);
  7241.         else
  7242.           if (ca + l - 1 < c2)
  7243.             printf ("%s", pal);
  7244.           else
  7245.             printf ("\n%*c%s", (ca = c1) - 1, ESPACIO, pal);
  7246.       ca += l;
  7247.       if (ca < c2)
  7248.         {
  7249.           putchar (' ');
  7250.           ca++;
  7251.         }
  7252.     }
  7253.   putchar ('\n');
  7254. } /* escribir */
  7255. ende
  7256. end lección 7
  7257.  
  7258. ; LECCION 8
  7259. begin
  7260. begine " PROGRAMA PARA PROBAR OPERACIONES CON UNA LISTA "
  7261. /*
  7262.   Ya dijimos en la teoría de la lección anterior que a veces necesitamos
  7263.   utilizar arrays dinámicos (su tamaño se determina en tiempo de ejecución)
  7264.   en vez de arrays estáticos (su tamaño se determina en tiempo de compilación);
  7265.   pero en ambos casos necesitamos saber el tamaño del array y en muchas apli-
  7266.   caciones no se sabe. Este problema se soluciona con las estructuras dinámi-
  7267.   cas de datos, en ellas vamos asignando y liberando memoria conforme a nues-
  7268.   tras necesidades.
  7269.  
  7270.   La forma más simple de relacionar o enlazar un conjunto de elementos es
  7271.   alinearlos formando una única lista pues, en este caso, se necesita única-
  7272.   mente un enlace por elemento, para referenciar su sucesor.
  7273.  
  7274.   Una lista la podemos representar gráficamente del siguiente modo:
  7275.  
  7276.     puntero a cabeza
  7277.            |
  7278.            |
  7279.            v
  7280.     ┌─────────────┐    ┌─────────────┐                       ┌─────────────┐
  7281.     │ información │    │ información │                       │ información │
  7282.     ├─────────────┤    ├─────────────┤                       ├─────────────┤
  7283.     │   puntero ------>│   puntero ------>   ......   ------>│     NULL    │
  7284.     └─────────────┘    └─────────────┘                       └─────────────┘
  7285.  
  7286.   En este programa se trabaja con una lista como la mostrada. Pero también
  7287.   se utiliza mucho otras listas más complejas:
  7288.  
  7289.    - Lista circular: el puntero al siguiente nodo del último nodo apunta
  7290.      al primer nodo. En este caso el puntero que apunta a la lista, apunta
  7291.      al último nodo de la lista, y se accede al primer elemento de la lista
  7292.      como el puntero siguiente al puntero a la lista.
  7293.  
  7294.    - Lista doblemente enlazada: en cada nodo, además de haber un puntero
  7295.      al siguiente nodo, también hay un puntero al anterior nodo. Puede
  7296.      ser lineal o circular.
  7297.  
  7298.    - Listas ordenadas: están ordenadas por algún criterio, de este modo
  7299.      las búsquedas (para consultas, inserciones, borrados, ...) son más
  7300.      rápidas.
  7301.  
  7302.    - Pilas y colas: son listas lineales simplemente enlazadas y circulares
  7303.      en las que la consulta de un nodo implica la supresión de ese nodo y
  7304.      sigue una regla: en las pilas se saca aquel elemento que entró el últi-
  7305.      mo y en las colas se saca aquel elemento que entró el último.
  7306.  
  7307.   Observaciones sobre el programa:
  7308.  
  7309.     1) Si pasamos un puntero por valor como argumento a una función (por
  7310.     ejemplo, parbol de la función main) podemos modificar el contenido de
  7311.     la dirección a la que apunta pero no la dirección a la que apunta. Para
  7312.     poder modificar también la dirección a la que apunta ese puntero pasado
  7313.     como argumento tenemos que pasarlo por referencia, es decir, pasar su
  7314.     dirección. Si la función empezar_lista() la hubiésemos definido así:
  7315.  
  7316.     void empezar_lista (pnl plist)
  7317.     {
  7318.       plist = NULL;
  7319.     }
  7320.  
  7321.     estamos asignando el valor NULL a la variable local plist pero no a la
  7322.     variable plista de la función main, con lo cual el programa no funcionaría
  7323.     correctamente.
  7324.  
  7325.     2) La función strrchr() de la librería <string.h> ya la vimos en los dos
  7326.     ejemplos de la escritura alfabética de números en la lección anterior.
  7327.     Esta función acepta un string y un carácter, y devuelve un puntero a la
  7328.     primera ocurrencia de ese carácter en el string dado, si el carácter no
  7329.     se encuentra la función devuelve NULL.
  7330. */
  7331.  
  7332. /* Ficheros a incluir: */
  7333.  
  7334. #include <stdio.h>  /* printf (), scanf (), puts () */
  7335. #include <conio.h>  /* getch () */
  7336. #include <stdlib.h> /* exit () */
  7337. #include <alloc.h>  /* malloc (), free () */
  7338. #include <string.h> /* strchr () */
  7339.  
  7340. /* Estructuras y tipos globales: */
  7341.  
  7342. struct nodo_lista
  7343.   {
  7344.     int informacion;
  7345.     struct nodo_lista *psiguiente;
  7346.   };
  7347.  
  7348. typedef struct nodo_lista nl; /* nl: nodo lista */
  7349. typedef nl *pnl;              /* pnl: puntero a nodo lista */
  7350.  
  7351. /* Macros: */
  7352.  
  7353. #define inform(p) ((p)->informacion) /* información apuntada por p */
  7354. #define psig(p)   ((p)->psiguiente)  /* puntero al siguiente apuntado por p */
  7355.  
  7356. #define BOOLEAN int
  7357. #define TRUE    1
  7358. #define FALSE   0
  7359.  
  7360. #define ESC ''
  7361.  
  7362. #define intercambiar(x,y) { int z; z = (x); (x) = (y); (y) = z; }
  7363.  
  7364. /* Prototipos de las funciones: */
  7365.  
  7366. void empezar_lista (pnl *plist);
  7367. void terminar_lista (pnl *plist);
  7368. void crear_nodo_lista (pnl *pl);
  7369. void liberar_nodo_lista (pnl *pl);
  7370. void aniadir_a_lista (pnl *plist, int info);
  7371. BOOLEAN quitar_de_lista (pnl *plist, int info);
  7372. pnl buscar_en_lista (pnl plist, int info);
  7373. void listar_lista (pnl plist);
  7374. void ordenar_lista (pnl *plist);
  7375. void insertar_en_orden_en_lista (pnl *plist, pnl pl);
  7376. void insertar_delante_en_lista (pnl *plist, pnl pl, pnl p);
  7377. void insertar_detras_en_lista (pnl pl, pnl p);
  7378.  
  7379. /* Definición de funciones: */
  7380.  
  7381. /*
  7382.   Función principal.
  7383. */
  7384.  
  7385. void main (void)
  7386. {
  7387.   BOOLEAN salir = FALSE;
  7388.   char ch;
  7389.   pnl plista;
  7390.   int num;
  7391.  
  7392.   puts ("\nPROGRAMA PARA PROBAR OPERACIONES CON UNA LISTA.");
  7393.   empezar_lista (&plista);
  7394.   do
  7395.     {
  7396.       printf ("\nOperación a realizar (Añadir, Quitar, Buscar, Listar, Ordenar, Salir): ");
  7397.       do
  7398.         {
  7399.           ch = getch ();
  7400.         } while (strchr ("aAqQbBlLoOsS", ch) == NULL);
  7401.       switch (putch (ch))
  7402.         {
  7403.           case 'a':
  7404.           case 'A':
  7405.             printf ("\n\nIntroduce número a añadir: ");
  7406.             scanf ("%d", &num);
  7407.             aniadir_a_lista (&plista, num);
  7408.             break;
  7409.           case 'q':
  7410.           case 'Q':
  7411.             printf ("\n\nIntroduce número a quitar: ");
  7412.             scanf ("%d", &num);
  7413.             if (quitar_de_lista (&plista, num))
  7414.               printf ("\nNúmero %d quitado de la lista.\n", num);
  7415.             else
  7416.               printf ("\nEl número %d no se encuentra en la lista.\n", num);
  7417.             break;
  7418.           case 'b':
  7419.           case 'B':
  7420.             printf ("\n\nIntroduce número a buscar: ");
  7421.             scanf ("%d", &num);
  7422.             printf ("\nNúmero %d%sencontrado.\n", num,
  7423.                     buscar_en_lista (plista, num) != NULL ? " " : " no ");
  7424.             break;
  7425.           case 'l':
  7426.           case 'L':
  7427.             listar_lista (plista);
  7428.             break;
  7429.           case 'o':
  7430.           case 'O':
  7431.             ordenar_lista (&plista);
  7432.             listar_lista (plista);
  7433.             break;
  7434.           case 's':
  7435.           case 'S':
  7436.           case ESC:
  7437.             salir = TRUE;
  7438.         }
  7439.     } while (! salir);
  7440.   terminar_lista (&plista);
  7441. }
  7442.  
  7443. /*
  7444.   La primera operación que hay que hacer siempre con una lista es inicializarla,
  7445.   esto consiste en asignar NULL al puntero que apunta a la cabeza de la lista
  7446. */
  7447.  
  7448. void empezar_lista (pnl *plist)
  7449. {
  7450.   *plist = NULL;
  7451. }
  7452.  
  7453. /*
  7454.   Antes de acabar el programa es conveniente liberar la memoria asignada
  7455.   dinámicamente. Para ello, lo único que se hace es recorrer toda la
  7456.   lista liberando cada nodo con free().
  7457. */
  7458.  
  7459. void terminar_lista (pnl *plist)
  7460. {
  7461.   pnl p;
  7462.  
  7463.   p = *plist;
  7464.   while (p) /* recorre toda la lista liberando cada nodo */
  7465.     {
  7466.       *plist = psig (p);
  7467.       liberar_nodo_lista (&p);
  7468.       p = *plist;
  7469.     }
  7470. }
  7471.  
  7472. /*
  7473.   Asigna memoria para un nodo de la lista.
  7474.   Si no hay suficiente memoria, el programa presenta un mensaje de error
  7475.   y termina.
  7476. */
  7477.  
  7478. void crear_nodo_lista (pnl *pl)
  7479. {
  7480.   if ((*pl = (pnl) malloc (sizeof (nl))) == NULL)
  7481.     {
  7482.       printf ("\nERROR: Memoria insuficiente.");
  7483.       exit (1);
  7484.     }
  7485. }
  7486.  
  7487. /*
  7488.   Libera memoria asignada con malloc, de este modo esta memoria puede
  7489.   ser reutilizada.
  7490. */
  7491.  
  7492. void liberar_nodo_lista (pnl *pl)
  7493. {
  7494.   free (*pl);
  7495. }
  7496.  
  7497. /*
  7498.   Añade un nodo al principio de la lista. Al tratarse de una lista desordenada
  7499.   no tiene importancia el lugar en que insertamos el nodo; sin embargo, inser-
  7500.   tarlo al principio es la inserción más rápida.
  7501. */
  7502.  
  7503. void aniadir_a_lista (pnl *plist, int info)
  7504. {
  7505.   pnl p;
  7506.  
  7507.   crear_nodo_lista (&p);
  7508.   inform (p) = info;
  7509.   psig (p) = *plist;
  7510.   *plist = p;
  7511. }
  7512.  
  7513. /*
  7514.   Quita el primer nodo encontrado que contenga el valor info en el
  7515.   campo información del nodo.
  7516.   Devuelve TRUE si se borró el elemento o FALSE en caso contrario.
  7517. */
  7518.  
  7519. BOOLEAN quitar_de_lista (pnl *plist, int info)
  7520. {
  7521.   pnl p = *plist, pant = NULL;
  7522.   BOOLEAN elemento_quitado;
  7523.  
  7524.   while (p != NULL && inform (p) != info) /* busca elemento */
  7525.     {
  7526.       pant = p;
  7527.       p = psig (p);
  7528.     }
  7529.  
  7530.   if (p == NULL) /* si hemos llegado al final de la lista, elemento no encontrado */
  7531.     elemento_quitado = FALSE;
  7532.   else
  7533.     {
  7534.       elemento_quitado = TRUE;
  7535.       (pant == NULL ? *plist : psig (pant)) = psig (p); /* dos casos: primer nodo u otro nodo */
  7536.       liberar_nodo_lista (&p);
  7537.     }
  7538.  
  7539.   return (elemento_quitado);
  7540. }
  7541.  
  7542. /*
  7543.   Devuelve TRUE si info se encuentra en un nodo de la lista, y FALSE en
  7544.   caso contrario.
  7545. */
  7546.  
  7547. pnl buscar_en_lista (pnl plist, int info)
  7548. {
  7549.   pnl p = plist;
  7550.  
  7551.   while (p != NULL && inform (p) != info) /* búsqueda del elemento en la lista */
  7552.     p = psig (p);
  7553.  
  7554.   return (p); /* p valdrá NULL si info no se ha encontrado */
  7555. }
  7556.  
  7557. /*
  7558.   Imprime todos los elementos de la lista.
  7559. */
  7560.  
  7561. void listar_lista (pnl plist)
  7562. {
  7563.   printf ("\n\nElementos en lista: ");
  7564.   if (plist == NULL)
  7565.     printf ("(ninguno)");
  7566.   else
  7567.     {
  7568.       pnl p;
  7569.       for (p = plist; p != NULL; p = psig (p)) /* recorre todos los nodos */
  7570.         printf ("%d ", inform(p));
  7571.     }
  7572.   puts (""); /* esta sentencia es equivalente a putch ('\n') y printf ("\n") */
  7573. }
  7574.  
  7575. /*
  7576.   Ordena la lista de menor a mayor.
  7577. */
  7578.  
  7579. void ordenar_lista (pnl *plist)
  7580. {
  7581.   pnl p1, p2;
  7582.  
  7583.   if (*plist != NULL)
  7584.     {
  7585.       p1 = psig (*plist);
  7586.       psig (*plist) = NULL;
  7587.       while (p1 != NULL) /* a partir de segundo nodo, va insertando ... */
  7588.         { /* ... cada nodo en la primera parte de la lista de una forma ordenada */
  7589.           p2 = psig (p1);
  7590.           insertar_en_orden_en_lista (plist, p1);
  7591.           p1 = p2;
  7592.         }
  7593.     }
  7594. }
  7595.  
  7596. /*
  7597.   Inserta el nodo pl en una lista ordenada de menor a mayor.
  7598. */
  7599.  
  7600. void insertar_en_orden_en_lista (pnl *plist, pnl pl)
  7601. {
  7602.   psig (pl) = NULL;
  7603.   if (*plist == NULL)
  7604.     *plist = pl;
  7605.   else
  7606.     {
  7607.       pnl p = *plist;
  7608.       while (inform (p) < inform (pl) && psig (p) != NULL) /* busca sitio donde insertar */
  7609.         p = psig (p);
  7610.       if (inform (p) >= inform (pl)) /* dos formas de insertar */
  7611.         insertar_delante_en_lista (plist, p, pl);
  7612.       else
  7613.         insertar_detras_en_lista (p, pl);
  7614.     }
  7615. }
  7616.  
  7617. /*
  7618.   Inserta el nodo p delante del nodo pl en lista encabezada por plist.
  7619. */
  7620.  
  7621. void insertar_delante_en_lista (pnl *plist, pnl pl, pnl p)
  7622. {
  7623.   if (pl == *plist)
  7624.     {
  7625.       psig (p) = *plist;
  7626.       *plist = p;
  7627.     }
  7628.   else
  7629.     {
  7630.       psig (p) = psig (pl);
  7631.       psig (pl) = p;
  7632.       intercambiar (inform (pl), inform (p));
  7633.     }
  7634. }
  7635.  
  7636. /*
  7637.   Inserta el nodo p detrás del nodo pl.
  7638. */
  7639.  
  7640. void insertar_detras_en_lista (pnl pl, pnl p)
  7641. {
  7642.   psig (p) = psig (pl);
  7643.   psig (pl) = p;
  7644. }
  7645.  
  7646. #undef inform
  7647. #undef psig
  7648. #undef BOOLEAN
  7649. #undef TRUE
  7650. #undef FALSE
  7651. #undef ESC
  7652. #undef intercambiar
  7653. ende
  7654. begine " PROGRAMA PARA PROBAR OPERACIONES CON UN ARBOL "
  7655. /*
  7656.   La estructura de árbol binario (aquél en que cada nodo tiene dos hijos)
  7657.   suele ser más utilizada que las listas ordenadas (no tiene sentido un
  7658.   árbol no ordenado) porque el acceso a un determinado elemento es mucho
  7659.   más rápido (en términos de media) que en tales listas.
  7660.  
  7661.   Un árbol binario lo podemos representar gráficamente de la siguiente forma:
  7662.  
  7663.                               puntero a cabeza
  7664.                                      |
  7665.                                      |
  7666.                                      v
  7667.                               ┌─────────────┐
  7668.                               │ información │
  7669.                               ├──────┬──────┤
  7670.                               │ pizq │ pder │
  7671.                               └──────┴──────┘
  7672.                                  /      \
  7673.                                /          \
  7674.                              /              \
  7675.                            /                  \
  7676.                       ┌─────────────┐  ┌─────────────┐
  7677.                       │ información │  │ información │
  7678.                       ├──────┬──────┤  ├──────┬──────┤
  7679.                       │ pizq │ pder │  │ pizq │ pder │
  7680.                       └──────┴──────┘  └──────┴──────┘
  7681.                          /      \         /       \
  7682.                        /          \     /           \
  7683.                      ...          ... ...           ...
  7684. */
  7685.  
  7686. /* Ficheros a incluir: */
  7687.  
  7688. #include <stdio.h>  /* printf (), scanf (), puts () */
  7689. #include <conio.h>  /* getch () */
  7690. #include <stdlib.h> /* exit () */
  7691. #include <alloc.h>  /* malloc (), free () */
  7692. #include <string.h> /* strchr () */
  7693.  
  7694. /* Estructuras y tipos globales: */
  7695.  
  7696. struct nodo_arbol
  7697.   {
  7698.     int informacion;
  7699.     struct nodo_arbol *pizquierda, *pderecha;
  7700.   };
  7701.  
  7702. typedef struct nodo_arbol na; /* na: nodo árbol */
  7703. typedef na *pna;              /* pna: puntero a nodo árbol */
  7704.  
  7705. /* Macros: */
  7706.  
  7707. #define inform(p) ((p)->informacion) /* información apuntada por p */
  7708. #define pizq(p)   ((p)->pizquierda)  /* puntero al nodo izquierdo apuntado por p */
  7709. #define pder(p)   ((p)->pderecha)    /* puntero al nodo derecho apuntado por p */
  7710.  
  7711. #define BOOLEAN int
  7712. #define TRUE    1
  7713. #define FALSE   0
  7714.  
  7715. #define ESC ''
  7716.  
  7717. /* Prototipos de las funciones: */
  7718.  
  7719. void empezar_arbol (pna *parb);
  7720. void terminar_arbol (pna *parb);
  7721. void crear_nodo_arbol (pna *pa);
  7722. void liberar_nodo_arbol (pna *pa);
  7723. void aniadir_a_arbol (pna *parb, int info);
  7724. BOOLEAN quitar_de_arbol (pna *parb, int info);
  7725. void suprimir_nodo_de_arbol (pna *p);
  7726. pna buscar_en_arbol (pna parb, int info);
  7727. void imprimir_arbol_en_preorden (pna parb);
  7728. void imprimir_arbol_en_postorden (pna parb);
  7729. void imprimir_arbol_en_inorden (pna parb);
  7730.  
  7731. /* Definición de funciones: */
  7732.  
  7733. /*
  7734.   Función principal.
  7735. */
  7736.  
  7737. void main (void)
  7738. {
  7739.   BOOLEAN salir = FALSE;
  7740.   char ch;
  7741.   pna parbol;
  7742.   int num;
  7743.  
  7744.   puts ("\nPROGRAMA PARA PROBAR OPERACIONES CON UN ARBOL.");
  7745.   empezar_arbol (&parbol);
  7746.   do
  7747.     {
  7748.       printf ("\nOperación a realizar (Añadir, Quitar, Buscar, Listar, Salir): ");
  7749.       do
  7750.         {
  7751.           ch = getch ();
  7752.         } while (strchr ("aAqQbBlLsS", ch) == NULL);
  7753.       switch (putch (ch))
  7754.         {
  7755.           case 'a':
  7756.           case 'A':
  7757.             printf ("\n\nIntroduce número a añadir: ");
  7758.             scanf ("%d", &num);
  7759.             aniadir_a_arbol (&parbol, num);
  7760.             break;
  7761.           case 'q':
  7762.           case 'Q':
  7763.             printf ("\n\nIntroduce número a quitar: ");
  7764.             scanf ("%d", &num);
  7765.             if (quitar_de_arbol (&parbol, num))
  7766.               printf ("\nNúmero %d quitado del árbol.\n", num);
  7767.             else
  7768.               printf ("\nEl número %d no se encuentra en el árbol.\n", num);
  7769.             break;
  7770.           case 'b':
  7771.           case 'B':
  7772.             printf ("\n\nIntroduce número a buscar: ");
  7773.             scanf ("%d", &num);
  7774.             printf ("\nNúmero %d%sencontrado.\n", num,
  7775.                     buscar_en_arbol (parbol, num) != NULL ? " " : " no ");
  7776.             break;
  7777.           case 'l':
  7778.           case 'L':
  7779.             printf ("\nTipo de recorrido (Preorden, posTorden, Inorden): ");
  7780.             do
  7781.               {
  7782.                 ch = getch ();
  7783.               } while (strchr ("pPtTiI", ch) == NULL);
  7784.             putch (ch);
  7785.             printf ("\n\nElementos en árbol: ");
  7786.             if (parbol == NULL)
  7787.               printf ("(ninguno)");
  7788.             else
  7789.               switch (ch)
  7790.                 {
  7791.                   case 'p':
  7792.                   case 'P':
  7793.                     imprimir_arbol_en_preorden (parbol);
  7794.                     break;
  7795.                   case 't':
  7796.                   case 'T':
  7797.                     imprimir_arbol_en_postorden (parbol);
  7798.                     break;
  7799.                   case 'i':
  7800.                   case 'I':
  7801.                     imprimir_arbol_en_inorden (parbol);
  7802.                     break;
  7803.                 }
  7804.             puts ("");
  7805.             break;
  7806.           case 's':
  7807.           case 'S':
  7808.           case ESC:
  7809.             salir = TRUE;
  7810.         }
  7811.     } while (! salir);
  7812.   terminar_arbol (&parbol);
  7813. }
  7814.  
  7815. /*
  7816.   La primera operación que hay que hacer siempre con un árbol es inicializarlo,
  7817.   esto consiste en asignar NULL al puntero que apunta a la cabeza del árbol.
  7818. */
  7819.  
  7820. void empezar_arbol (pna *parb)
  7821. {
  7822.   *parb = NULL;
  7823. }
  7824.  
  7825. /*
  7826.   Antes de acabar el programa es conveniente liberar la memoria asignada
  7827.   dinámicamente. Para ello, lo único que se hace es recorrer todo el
  7828.   árbol liberando cada nodo con free().
  7829. */
  7830.  
  7831. void terminar_arbol (pna *parb)
  7832. {
  7833.   if (*parb != NULL)
  7834.     {
  7835.       terminar_arbol (&pizq(*parb));
  7836.       terminar_arbol (&pder(*parb));
  7837.       liberar_nodo_arbol (parb);
  7838.     }
  7839. }
  7840.  
  7841. /*
  7842.   Asigna memoria para un nodo del árbol.
  7843.   Si no hay suficiente memoria, el programa presenta un mensaje de error
  7844.   y termina.
  7845. */
  7846.  
  7847. void crear_nodo_arbol (pna *pa)
  7848. {
  7849.   if ((*pa = (pna) malloc (sizeof (na))) == NULL)
  7850.     {
  7851.       printf ("\nERROR: Memoria insuficiente.");
  7852.       exit (1);
  7853.     }
  7854. }
  7855.  
  7856. /*
  7857.   Libera memoria asignada con malloc, de este modo esta memoria puede
  7858.   ser reutilizada.
  7859. */
  7860.  
  7861. void liberar_nodo_arbol (pna *pa)
  7862. {
  7863.   free (*pa);
  7864. }
  7865.  
  7866. /*
  7867.   Para ver cómo se inserta en lo árboles, veamos cómo se insertarían
  7868.   los siguientes números en un árbol donde información es un número
  7869.   entero:
  7870.  
  7871.     3 10 2 -1 0 3 8 20
  7872.  
  7873.   Paso 0:
  7874.               parbol
  7875.                 │
  7876.                 v
  7877.                NULL
  7878.   Paso 1:
  7879.               parbol
  7880.                 │
  7881.                 v
  7882.                 3
  7883.   Paso 2:
  7884.               parbol
  7885.                 │
  7886.                 v
  7887.                 3
  7888.                  \
  7889.                   10
  7890.   Paso 3:
  7891.               parbol
  7892.                 │
  7893.                 v
  7894.                 3
  7895.                / \
  7896.               2   10
  7897.   Paso 4:
  7898.               parbol
  7899.                 │
  7900.                 v
  7901.                 3
  7902.                / \
  7903.               2   10
  7904.              /
  7905.            -1
  7906.   Paso 5:
  7907.               parbol
  7908.                 │
  7909.                 v
  7910.                 3
  7911.                / \
  7912.               2   10
  7913.              /
  7914.            -1
  7915.              \
  7916.               0
  7917.   Paso 6:
  7918.               parbol
  7919.                 │
  7920.                 v
  7921.                 3
  7922.                / \
  7923.               2   10
  7924.              /   /
  7925.            -1   3
  7926.              \
  7927.               0
  7928.   Paso 7:
  7929.               parbol
  7930.                 │
  7931.                 v
  7932.                 3
  7933.                / \
  7934.               2   10
  7935.              /   /
  7936.            -1   3
  7937.              \   \
  7938.               0   8
  7939.   Paso 8:
  7940.               parbol
  7941.                 │
  7942.                 v
  7943.                 3
  7944.                / \
  7945.               2   10
  7946.              /   /  \
  7947.            -1   3    20
  7948.              \   \
  7949.               0   8
  7950.  
  7951. */
  7952.  
  7953. void aniadir_a_arbol (pna *parb, int info)
  7954. {
  7955.   pna p, pant; /* punteros utilizados para la búsqueda */
  7956.   pna pa; /* puntero que apunta a nuevo nodo creado y que hay que insertar */
  7957.  
  7958.   crear_nodo_arbol (&pa);
  7959.   inform (pa) = info;
  7960.   pizq (pa) = pder (pa) = NULL;
  7961.  
  7962.   p = *parb;
  7963.   pant = NULL;
  7964.   while (p != NULL) /* busca lugar de inserción */
  7965.     {
  7966.       pant = p;
  7967.       if (inform (p) > info)
  7968.         p = pizq (p);
  7969.       else
  7970.         p = pder (p);
  7971.     }
  7972.  
  7973.   if (pant == NULL) /* inserta en lugar correspondiente */
  7974.     *parb = pa;
  7975.   else if (inform (pant) > info)
  7976.     pizq (pant) = pa;
  7977.   else
  7978.     pder (pant) = pa;
  7979. }
  7980.  
  7981. /*
  7982.   Quita el primer nodo encontrado que contenga el valor info en el
  7983.   campo información del nodo.
  7984.   Devuelve TRUE si se borró el elemento o FALSE en caso contrario.
  7985. */
  7986.  
  7987. BOOLEAN quitar_de_arbol (pna *parb, int info)
  7988. {
  7989.   pna p = *parb, pant = NULL;
  7990.   BOOLEAN encontrado = FALSE;
  7991.  
  7992.   while (p != NULL && ! encontrado) /* busca nodo a borrar */
  7993.     if (inform (p) == info)
  7994.       encontrado = TRUE;
  7995.     else
  7996.       {
  7997.         pant = p;
  7998.         if (inform (p) > info)
  7999.           p = pizq (p);
  8000.         else
  8001.           p = pder (p);
  8002.       }
  8003.  
  8004.   if (encontrado) /* llama a la función suprimir con puntero apuntado a nodo a quitar */
  8005.     if (p == *parb)
  8006.       suprimir_nodo_de_arbol (parb);
  8007.     else if (pizq (pant) == p)
  8008.       suprimir_nodo_de_arbol (&pizq(pant));
  8009.     else
  8010.       suprimir_nodo_de_arbol (&pder(pant));
  8011.  
  8012.   return (encontrado);
  8013. }
  8014.  
  8015. /*
  8016.   Suprime nodo de árbol apuntado por p.
  8017.   Esta función es llamada por la función quitar_de_arbol.
  8018.  
  8019.   Hay tres situaciones diferentes a la hora de suprimir un nodo, a saber:
  8020.  
  8021.   Sea el árbol compuesto de números reales:
  8022.  
  8023.                                         5
  8024.                                       /   \
  8025.                                      3     8
  8026.                                     / \   / \
  8027.                                    2   4 6   9
  8028.                                   /     / \
  8029.                                  1    5.5  7
  8030.                                           /
  8031.                                          6.5
  8032.  
  8033.   Situación 1: suprimir nodo sin hijo derecho; por ejemplo, el nodo 7, en
  8034.   este caso el puntero p es el puntero derecho del nodo 6; la acción a
  8035.   realizar es: p = pizq (p).
  8036.  
  8037.   Situación 2: suprimir nodo sin hijo izquierdo; por ejemplo, el nodo 1, en
  8038.   este caso el puntero p es el puntero izquierdo del nodo 2; la acción a
  8039.   realizar es: p = pder (p).
  8040.  
  8041.   Situación 3: suprimir nodo intermedio, es decir, nodo con hijos izquierdo
  8042.   y derecho; por ejemplo, el nodo 8, en este caso el puntero p es el
  8043.   puntero derecho del nodo 5; la acción a realizar es sustituir el contenido
  8044.   del nodo a suprimir (nodo 8) por el mayor contenido entre los nodos del
  8045.   subárbol izquierdo, que se calcula como el nodo más a la derecha a partir
  8046.   del nodo izquierdo del nodo a suprimir, en este caso el nodo izquierdo es
  8047.   6 y recorriendo los punteros a la derecha hasta llegar al final, nos
  8048.   paramos en el nodo 7, copiamos el contenido del nodo 7 en el nodo 8 y
  8049.   ahora tenemos que suprimir un nodo (el 7 en este caso) que ya pertenece
  8050.   a uno de los dos casos anteriores.
  8051. */
  8052.  
  8053. void suprimir_nodo_de_arbol (pna *p)
  8054. {
  8055.   pna ptemp = *p;
  8056.  
  8057.   if (pder (*p) == NULL) /* situación 1 */
  8058.     *p = pizq (*p);
  8059.   else if (pizq (*p) == NULL) /* situación 2 */
  8060.     *p = pder (*p);
  8061.   else /* situación 3 */
  8062.     {
  8063.       pna pant = *p;
  8064.       ptemp = pizq (*p);
  8065.       while (pder (ptemp) != NULL) /* recorremos punteros a derecha a partir ... */
  8066.         { /* ... puntero a izquierda del nodo apuntado por el puntero a suprimir */
  8067.           pant = ptemp;
  8068.           ptemp = pder (ptemp);
  8069.         }
  8070.       inform (*p) = inform (ptemp);
  8071.       if (pant == *p)
  8072.         pizq (pant) = pizq (ptemp);
  8073.       else
  8074.         pder (pant) = pizq (ptemp);
  8075.     }
  8076.  
  8077.   liberar_nodo_arbol (&ptemp);
  8078. }
  8079.  
  8080. /*
  8081.   Devuelve un puntero al nodo del árbol que contiene dat. Si ningún nodo
  8082.   del árbol contiene el dato dat, devuelve NULL.
  8083. */
  8084.  
  8085. pna buscar_en_arbol (pna parb, int info)
  8086. {
  8087.   pna p = parb;
  8088.   BOOLEAN encontrado = FALSE;
  8089.  
  8090.   while (p != NULL && ! encontrado)
  8091.     if (inform (p) == info)
  8092.       encontrado = TRUE;
  8093.     else if (inform (p) > info)
  8094.       p = pizq (p);
  8095.     else
  8096.       p = pder (p);
  8097.  
  8098.   return (p); /* p valdrá NULL si info no se ha encontrado */
  8099. }
  8100.  
  8101. /*
  8102.   Imprime todos los elementos del árbol haciendo un recorrido en preorden.
  8103. */
  8104.  
  8105. void imprimir_arbol_en_preorden (pna parb)
  8106. {
  8107.   if (parb != NULL)
  8108.     {
  8109.       printf ("%d ", inform (parb));
  8110.       imprimir_arbol_en_preorden (pizq (parb)); /* imprime subárbol izquierdo */
  8111.       imprimir_arbol_en_preorden (pder (parb)); /* imprime subárbol derecho */
  8112.     }
  8113. }
  8114.  
  8115. /*
  8116.   Imprime todos los elementos del árbol haciendo un recorrido en postorden.
  8117. */
  8118.  
  8119. void imprimir_arbol_en_postorden (pna parb)
  8120. {
  8121.   if (parb != NULL)
  8122.     {
  8123.       imprimir_arbol_en_postorden (pizq (parb)); /* imprime subárbol izquierdo */
  8124.       imprimir_arbol_en_postorden (pder (parb)); /* imprime subárbol derecho */
  8125.       printf ("%d ", inform (parb));
  8126.     }
  8127. }
  8128.  
  8129. /*
  8130.   Imprime todos los elementos del árbol haciendo un recorrido en inorden.
  8131.   Si el árbol está ordenado, este recorrido imprime los elmentos ordenados.
  8132. */
  8133.  
  8134. void imprimir_arbol_en_inorden (pna parb)
  8135. {
  8136.   if (parb != NULL)
  8137.     {
  8138.       imprimir_arbol_en_inorden (pizq (parb)); /* imprime subárbol izquierdo */
  8139.       printf ("%d ", inform (parb));
  8140.       imprimir_arbol_en_inorden (pder (parb)); /* imprime subárbol derecho */
  8141.     }
  8142. }
  8143.  
  8144. #undef inform
  8145. #undef pder
  8146. #undef pizq
  8147. #undef BOOLEAN
  8148. #undef TRUE
  8149. #undef FALSE
  8150. #undef ESC
  8151. ende
  8152. begine " PROGRAMA PARA PROBAR OPERACIONES CON UNA TABLA HASH "
  8153. /*
  8154.   Uno de los métodos más rápidos para acceder a elementos de tipo string
  8155.   que estén ordenados según algún criterio es mediante la técnica hash. En
  8156.   realidad, los elementos no tienen porqué ser de tipo string, pero es más
  8157.   utilizado en estos casos.
  8158.  
  8159.   Esta técnica consiste en lo siguiente:
  8160.  
  8161.     1. Tenemos una tabla del tamaño que queramos. Cuantas más entradas
  8162.        tenga la tabla, el método será más eficiente.
  8163.  
  8164.     2. Cada elemento de la tabla es un puntero a otra tabla hash, a una
  8165.        lista (preferible ordenada), a un árbol o a cualquier otra estructura
  8166.        donde guardar los strings correspondiente a esa entrada de la tabla.
  8167.        En este programa, cada elemento de la tabla apunta a la cabeza de una
  8168.        lista ordenada de menor a mayor de strings. También hubiera sido una
  8169.        buena estructura un árbol binario.
  8170.  
  8171.     3. Cuando queremos acceder a un determinado string (para búsqueda,
  8172.        inserción, supresión, ... del string) obtenemos su clave hash; esta
  8173.        clave hash es un número que corresponde a un índice de la tabla. Una
  8174.        vez obtenida la clave hash del string, ya tenemos el índice de la
  8175.        tabla en el cual está (debe de estar) el string. Una buena clave para
  8176.        los strings es el resto (módulo) de dividir la suma de los valores
  8177.        ascii de los caracteres del string entre el número de elementos de
  8178.        la tabla. De esta forma la clave hash siempre corresponde a un índice
  8179.        válido de la tabla. Según esta fórmula los strings "abc" y "bca"
  8180.        tienen la misma clave, esto significa, que estos dos strings están
  8181.        en una misma lista (recordar que cada entrada de la tabla apunta a
  8182.        una lista distinta de strings). En este programa también se observa
  8183.        que la información contenido en cada nodo de la lista es un puntero
  8184.        al string, donde la memoria ocupada por este string es asignada
  8185.        dinámicamente y por lo tanto ha de ser liberada dinámicamente.
  8186.  
  8187.   Observaciones:
  8188.  
  8189.     1) Las funciones de operaciones con lista son similares a las que hemos
  8190.        visto en el primer ejemplo. Allí se explican más que aquí.
  8191.  
  8192.     2) Hemos utilizado dos funciones de la librería <string.h> que merecen
  8193.        una explicación:
  8194.  
  8195.        · int strcmp (const char *s1, const char *s2);
  8196.  
  8197.         Esta función compara los strings s1 y s2. Devuelve uno de los
  8198.         siguientes valores:
  8199.  
  8200.           < 0 si s1 es menor que s2
  8201.          == 0 si s1 es igual que s2
  8202.           > 0 si s1 es mayor que s2
  8203.  
  8204.        · char *strdup (const char *s);
  8205.  
  8206.         Esta función obtiene una copia duplicada de s, o copia s a una
  8207.         nueva localización. Devuelve un puntero a s duplicado, o devuelve
  8208.         NULL si no se puedo asignar espacio para la copia. El programador
  8209.         es responsable de liberar el espacio asignado por strdup cuando ya
  8210.         no haga falta.
  8211.  
  8212.         Esta función es parecida a malloc() con la diferencia de que strdup
  8213.         además de reservar memoria, copia el string s a la nueva memoria
  8214.         reservada.
  8215. */
  8216.  
  8217. /* Ficheros a incluir: */
  8218.  
  8219. #include <conio.h>  /* getch ()                  */
  8220. #include <stdio.h>  /* printf (), scanf (), NULL */
  8221. #include <alloc.h>  /* malloc (), free ()        */
  8222. #include <string.h> /* strdup (), strcmp ()      */
  8223. #include <stdlib.h> /* exit ()                   */
  8224.  
  8225. /* Macros: */
  8226.  
  8227. #define NUM_ELEMENTOS_TABLA_HASH 255
  8228. #define NUM_MAX_CARACTERES_FRASE 255
  8229.  
  8230. #define BOOLEAN int
  8231. #define TRUE    1
  8232. #define FALSE   0
  8233.  
  8234. #define ESC 27
  8235.  
  8236. #define intercambiar(s1,s2) { char *s3; s3 = (s1); (s1) = (s2); (s2) = s3; }
  8237.  
  8238. /* Tipos de datos y macros: */
  8239.  
  8240. struct struct_nodolista
  8241.   {
  8242.     char *frase;
  8243.     struct struct_nodolista *psiguiente;
  8244.   };
  8245.  
  8246. typedef struct struct_nodolista nodolista;
  8247. typedef nodolista *p_nodolista;
  8248. typedef p_nodolista tabla_hash [NUM_ELEMENTOS_TABLA_HASH];
  8249.  
  8250. #define frase(p) ((p)->frase)
  8251. #define psig(p)  ((p)->psiguiente)
  8252.  
  8253. /* Prototipos de las funciones: */
  8254.  
  8255. int clave_hash (char *string);
  8256. void empezar_tabla_hash (tabla_hash tabla);
  8257. void terminar_tabla_hash (tabla_hash tabla);
  8258. void liberar_lista_tabla_hash (p_nodolista *plista);
  8259. void liberar_nodo_lista_tabla_hash (p_nodolista *pl);
  8260. void crear_nodo_lista (p_nodolista *pl);
  8261. void insertar_en_lista_tabla_hash (p_nodolista *plista, char *frase);
  8262. void insertar_en_orden_en_lista_tabla_hash (p_nodolista *plista, p_nodolista pl);
  8263. void insertar_delante_en_lista_tabla_hash (p_nodolista *plista,
  8264.                                            p_nodolista pl, p_nodolista p);
  8265. void insertar_detras_en_lista_tabla_hash (p_nodolista pl, p_nodolista p);
  8266. void insertar_en_tabla_hash (tabla_hash tabla, char *frase);
  8267. p_nodolista buscar_en_lista_tabla_hash (p_nodolista plista, char *frase);
  8268. p_nodolista buscar_en_tabla_hash (tabla_hash tabla, char *frase);
  8269. void listar_lista_tabla_hash (p_nodolista plista);
  8270. void listar_tabla_hash (tabla_hash tabla);
  8271. BOOLEAN quitar_de_lista_tabla_hash (p_nodolista *plista, char *frase);
  8272. BOOLEAN quitar_de_tabla_hash (tabla_hash tabla, char *frase);
  8273. void error_de_memoria (void);
  8274.  
  8275. /* Definición de las funciones: */
  8276.  
  8277. /*
  8278.   Función principal.
  8279. */
  8280.  
  8281. void prlec8_3 (void)
  8282. {
  8283.   tabla_hash tabla_hash;
  8284.   BOOLEAN salir = FALSE;
  8285.   char ch;
  8286.   char frase[NUM_MAX_CARACTERES_FRASE];
  8287.  
  8288.   puts ("\nPROGRAMA PARA PROBAR OPERACIONES CON UNA TABLA HASH.");
  8289.   empezar_tabla_hash (tabla_hash);
  8290.   do
  8291.     {
  8292.       printf ("\nOperación a realizar (Insertar, Quitar, Buscar, Listar, Salir): ");
  8293.       do
  8294.         {
  8295.           ch = getch ();
  8296.         } while (strchr ("iIqQbBlLsS", ch) == NULL);
  8297.       switch (putch (ch))
  8298.         {
  8299.           case 'i':
  8300.           case 'I':
  8301.             printf ("\n\nIntroduce frase a añadir: ");
  8302.             scanf ("%s", frase);
  8303.             insertar_en_tabla_hash (tabla_hash, frase);
  8304.             break;
  8305.           case 'q':
  8306.           case 'Q':
  8307.             printf ("\n\nIntroduce frase a quitar: ");
  8308.             scanf ("%s", frase);
  8309.             if (quitar_de_tabla_hash (tabla_hash, frase))
  8310.               printf ("\nFrase \"%s\" quitada de la tabla hash.\n", frase);
  8311.             else
  8312.               printf ("\nLa frase \"%s\" no se encuentra en la tabla hash.\n", frase);
  8313.             break;
  8314.           case 'b':
  8315.           case 'B':
  8316.             printf ("\n\nIntroduce frase a buscar: ");
  8317.             scanf ("%s", frase);
  8318.             printf ("\nFrase \"%s\"%sencontrado.\n", frase,
  8319.                     buscar_en_tabla_hash (tabla_hash, frase) != NULL ? " " : " no ");
  8320.             break;
  8321.           case 'l':
  8322.           case 'L':
  8323.             listar_tabla_hash (tabla_hash);
  8324.             break;
  8325.           case 's':
  8326.           case 'S':
  8327.           case ESC:
  8328.             salir = TRUE;
  8329.         }
  8330.     } while (! salir);
  8331.   terminar_tabla_hash (tabla_hash);
  8332. }
  8333.  
  8334. /*
  8335.   Devuelve la clave hash de un determinado string.
  8336. */
  8337.  
  8338. int clave_hash (char *string)
  8339. {
  8340.   register int suma_ascii;
  8341.   char *p;
  8342.  
  8343.   for (suma_ascii = 0, p = string; *p; p++)
  8344.     suma_ascii += *p;
  8345.  
  8346.   return ((suma_ascii) % (NUM_ELEMENTOS_TABLA_HASH));
  8347. }
  8348.  
  8349. /*
  8350.   Inicializa elementos de la tabla hash.
  8351. */
  8352.  
  8353. void empezar_tabla_hash (tabla_hash tabla)
  8354. {
  8355.   register int i;
  8356.  
  8357.   for (i = 0; i < NUM_ELEMENTOS_TABLA_HASH; i++)
  8358.     tabla[i] = NULL;
  8359. }
  8360.  
  8361. /*
  8362.   Libera memoria asignada dinámicamente en tabla hash.
  8363. */
  8364.  
  8365. void terminar_tabla_hash (tabla_hash tabla)
  8366. {
  8367.   register int i;
  8368.  
  8369.   for (i = 0; i < NUM_ELEMENTOS_TABLA_HASH; i++)
  8370.     liberar_lista_tabla_hash (&tabla[i]);
  8371. }
  8372.  
  8373. /*
  8374.   Libera memoria asignada dinámicamente de una lista.
  8375. */
  8376.  
  8377. void liberar_lista_tabla_hash (p_nodolista *plista)
  8378. {
  8379.   p_nodolista p;
  8380.  
  8381.   p = *plista;
  8382.   while (p != NULL)
  8383.     {
  8384.       *plista = psig (p);
  8385.       liberar_nodo_lista_tabla_hash (&p);
  8386.       p = *plista;
  8387.     }
  8388. }
  8389.  
  8390. /*
  8391.   Libera de memoria un nodo de una lista.
  8392. */
  8393.  
  8394. void liberar_nodo_lista_tabla_hash (p_nodolista *pl)
  8395. {
  8396.   if (frase (*pl) != NULL)
  8397.     free (frase (*pl));
  8398.   free (*pl);
  8399. }
  8400.  
  8401. /*
  8402.   Asigna memoria para un nodo de una lista.
  8403. */
  8404.  
  8405. void crear_nodo_lista (p_nodolista *pl)
  8406. {
  8407.   if ((*pl = (p_nodolista) malloc (sizeof (nodolista))) == NULL)
  8408.     error_de_memoria ();
  8409. }
  8410.  
  8411. /*
  8412.   Inserta una frase en la tabla hash.
  8413. */
  8414.  
  8415. void insertar_en_lista_tabla_hash (p_nodolista *plista, char *frase)
  8416. {
  8417.   p_nodolista p;
  8418.  
  8419.   crear_nodo_lista (&p);
  8420.   if ((frase (p) = strdup (frase)) == NULL)
  8421.     error_de_memoria ();
  8422.   insertar_en_orden_en_lista_tabla_hash (plista, p);
  8423. }
  8424.  
  8425. /*
  8426.   Inserta una frase en orden en una lista.
  8427. */
  8428.  
  8429. void insertar_en_orden_en_lista_tabla_hash (p_nodolista *plista, p_nodolista pl)
  8430. {
  8431.   psig (pl) = NULL;
  8432.   if (*plista == NULL)
  8433.     *plista = pl;
  8434.   else
  8435.     {
  8436.       p_nodolista p = *plista;
  8437.       while (strcmp (frase (p), frase (pl)) < 0 && psig (p) != NULL)
  8438.         p = psig (p);
  8439.       if (strcmp (frase (p), frase (pl)) >= 0)
  8440.         insertar_delante_en_lista_tabla_hash (plista, p, pl);
  8441.       else
  8442.         insertar_detras_en_lista_tabla_hash (p, pl);
  8443.     }
  8444. }
  8445.  
  8446. /*
  8447.   Inserta el nodo p delante del nodo pl en lista encabezada por *plista.
  8448. */
  8449.  
  8450. void insertar_delante_en_lista_tabla_hash (p_nodolista *plista,
  8451.                                            p_nodolista pl, p_nodolista p)
  8452. {
  8453.   if (pl == *plista)
  8454.     {
  8455.       psig (p) = *plista;
  8456.       *plista = p;
  8457.     }
  8458.   else
  8459.     {
  8460.       psig (p) = psig (pl);
  8461.       psig (pl) = p;
  8462.       intercambiar (frase (pl), frase (p));
  8463.     }
  8464. }
  8465.  
  8466. /*
  8467.   Inserta el nodo p detrás del nodo pl.
  8468. */
  8469.  
  8470. void insertar_detras_en_lista_tabla_hash (p_nodolista pl, p_nodolista p)
  8471. {
  8472.   psig (p) = psig (pl);
  8473.   psig (pl) = p;
  8474. }
  8475.  
  8476. /*
  8477.   Inserta una frase en la tabla hash.
  8478. */
  8479.  
  8480. void insertar_en_tabla_hash (tabla_hash tabla, char *frase)
  8481. {
  8482.   int valor_hash;
  8483.  
  8484.   valor_hash = clave_hash (frase);
  8485.   insertar_en_lista_tabla_hash (&tabla[valor_hash], frase);
  8486. }
  8487.  
  8488. /*
  8489.   Busca una frase en una lista.
  8490. */
  8491.  
  8492. p_nodolista buscar_en_lista_tabla_hash (p_nodolista plista, char *frase)
  8493. {
  8494.   p_nodolista p = plista;
  8495.  
  8496.   while (p != NULL && strcmp (frase, frase (p)) < 0)
  8497.     p = psig (p);
  8498.  
  8499.   return (p != NULL && strcmp (frase, frase (p)) == 0 ? p : NULL);
  8500. }
  8501.  
  8502. /*
  8503.   Busca una frase en la tabla hash.
  8504. */
  8505.  
  8506. p_nodolista buscar_en_tabla_hash (tabla_hash tabla, char *frase)
  8507. {
  8508.   int valor_hash;
  8509.  
  8510.   valor_hash = clave_hash (frase);
  8511.   return (buscar_en_lista_tabla_hash (tabla[valor_hash], frase));
  8512. }
  8513.  
  8514. /*
  8515.   Lista todas las frases contenidas en una lista.
  8516. */
  8517.  
  8518. void listar_lista_tabla_hash (p_nodolista plista)
  8519. {
  8520.   p_nodolista p;
  8521.   for (p = plista; p != NULL; p = psig (p))
  8522.     puts (frase (p));
  8523. }
  8524.  
  8525. /*
  8526.   Lista todas las frases que hay en la tabla hash.
  8527. */
  8528.  
  8529. void listar_tabla_hash (tabla_hash tabla)
  8530. {
  8531.   register int i;
  8532.  
  8533.   printf ("\n\nElementos en listas de tabla hash:\n\n");
  8534.   for (i = 0; i < NUM_ELEMENTOS_TABLA_HASH; i++)
  8535.     listar_lista_tabla_hash (tabla[i]);
  8536.   puts ("");
  8537. }
  8538.  
  8539. /*
  8540.   Quita una frase de una lista.
  8541. */
  8542.  
  8543. BOOLEAN quitar_de_lista_tabla_hash (p_nodolista *plista, char *frase)
  8544. {
  8545.   p_nodolista p = *plista, pant = NULL;
  8546.   BOOLEAN elemento_quitado;
  8547.  
  8548.   while (p != NULL && strcmp (frase, frase (p)) < 0)
  8549.     {
  8550.       pant = p;
  8551.       p = psig (p);
  8552.     }
  8553.  
  8554.   if (p == NULL || strcmp (frase, frase (p)) != 0)
  8555.     elemento_quitado = FALSE;
  8556.   else
  8557.     {
  8558.       elemento_quitado = TRUE;
  8559.       if (pant == NULL)
  8560.         *plista = psig (p);
  8561.       else
  8562.         psig (pant) = psig (p);
  8563.       liberar_nodo_lista_tabla_hash (&p);
  8564.     }
  8565.  
  8566.   return (elemento_quitado);
  8567. }
  8568.  
  8569. /*
  8570.   Quita una frase de la tabla hash.
  8571. */
  8572.  
  8573. BOOLEAN quitar_de_tabla_hash (tabla_hash tabla, char *frase)
  8574. {
  8575.   int valor_hash;
  8576.  
  8577.   valor_hash = clave_hash (frase);
  8578.   return (quitar_de_lista_tabla_hash (&tabla[valor_hash], frase));
  8579. }
  8580.  
  8581. /*
  8582.   Imprime mensaje de error de memoria insuficiente y finaliza el programa.
  8583. */
  8584.  
  8585. void error_de_memoria (void)
  8586. {
  8587.   printf ("\nERROR: Memoria insuficiente.\n");
  8588.   getch ();
  8589.   exit (1);
  8590. }
  8591. ende
  8592. end lección 8
  8593.  
  8594. ; LECCION 9
  8595. begin
  8596. begine "COPIAR FICHERO UTILIZANDO FUNCIONES DE E/S DE ALTO NIVEL"
  8597. /*
  8598.   Este programa copia un fichero a otro fichero.
  8599.   Funcionalmente este programa es similar al copy del DOS o al cat del UNIX.
  8600.   Si el fichero de salida es con, se está haciendo un type del DOS.
  8601.   Si el fichero de entrada es con, recuerda que el carácter fin de fichero
  8602.   se escribe con CTRL-Z en el DOS.
  8603.  
  8604.   La función strcpy() copia el string pasado como segundo argumento en el
  8605.   string pasado como primer argumento. La función toupper() devuelve el
  8606.   carácter pasado como argumento en mayúscula.
  8607. */
  8608.  
  8609. #include <stdio.h>  /* printf (), gets (), fprintf (), stderr, EOF, fopen (),
  8610.                        fclose (), fgetc (), fputc (), NULL */
  8611. #include <conio.h>  /* getch () */
  8612. #include <string.h> /* strcpy () */
  8613. #include <ctype.h>  /* toupper () */
  8614.  
  8615. #define BOOLEAN int
  8616. #define TRUE    1
  8617. #define FALSE   0
  8618.  
  8619. #define ESC 27
  8620. #define ENTER '\r'
  8621.  
  8622. #define NUMMAXCARACTERES 255
  8623.  
  8624. void main (void)
  8625. {
  8626.   BOOLEAN salir = FALSE;
  8627.   char nombre_fichero_entrada[NUMMAXCARACTERES],
  8628.        nombre_fichero_salida[NUMMAXCARACTERES];
  8629.   FILE *pfe, *pfs; /* punteros a fichero de entrada y fichero de salida resp. */
  8630.   char ch;
  8631.  
  8632.   while (! salir)
  8633.     {
  8634.       puts ("\n\nCOPIAR FICHERO CON EL SISTEMA DE FICHEROS DE ALTO NIVEL:\n");
  8635.       printf ("Introduce nombre de fichero de entrada (ENTER o CON para teclado): ");
  8636.       gets (nombre_fichero_entrada);
  8637.       if (*nombre_fichero_entrada == '\0')
  8638.         strcpy (nombre_fichero_entrada, "con");
  8639.       printf ("Introduce nombre de fichero de salida (ENTER o CON para pantalla): ");
  8640.       gets (nombre_fichero_salida);
  8641.       if (*nombre_fichero_salida == '\0')
  8642.         strcpy (nombre_fichero_salida, "con");
  8643.  
  8644.       if ((pfe = fopen (nombre_fichero_entrada, "r")) == NULL)
  8645.         fprintf (stderr, "\nERROR: No es posible abrir el fichero de entrada %s.\n",
  8646.                  nombre_fichero_entrada);
  8647.       else if ((pfs = fopen (nombre_fichero_salida, "w")) == NULL)
  8648.         {
  8649.           fprintf (stderr, "\nERROR: No es posible abrir el fichero de salida %s.\n",
  8650.                   nombre_fichero_salida);
  8651.           fclose (pfe);
  8652.         }
  8653.       else
  8654.         {
  8655.           int c;
  8656.           while ((c = fgetc (pfe)) != EOF)
  8657.             fputc (c, pfs);
  8658.  
  8659.           fclose (pfe);
  8660.           fclose (pfs);
  8661.         }
  8662.  
  8663.       printf ("\n\n¿Desea copiar otro fichero (S o ENTER: Sí; N o ESC: No)? ");
  8664.       do
  8665.         {
  8666.           ch = getch ();
  8667.         } while (ch != ENTER && toupper (ch) != 'S' &&
  8668.                  ch != ESC && toupper (ch) != 'N');
  8669.       salir = ch == ESC || toupper (ch) == 'N';
  8670.     }
  8671. }
  8672. ende
  8673. begint
  8674. begine "COPIAR FICHERO UTILIZANDO FUNCIONES DE E/S DE BAJO NIVEL"
  8675. /*
  8676.   Este programa copia un fichero a otro fichero.
  8677.  
  8678.   Hace lo mismo que el programa anterior con la diferencia que éste utiliza
  8679.   funciones de E/S de bajo nivel.
  8680.  
  8681.   Aunque se podría haber hecho como el programa anterior leyendo y escribiendo
  8682.   caracteres uno a uno, leer y escribir un bloque de caracteres es bastante
  8683.   más rápido.
  8684. */
  8685.  
  8686. #include <stdio.h>    /* printf (), gets (), BUFSIZ */
  8687. #include <conio.h>    /* getch () */
  8688. #include <string.h>   /* strcpy () */
  8689. #include <ctype.h>    /* toupper () */
  8690. #include <fcntl.h>    /* O_RDONLY, O_WRONLY, O_TEXT */
  8691. #include <io.h>       /* open (), close (), read (), write () */
  8692. #include <sys\stat.h> /* S_IREAD, S_IWRITE */
  8693.  
  8694. #define BOOLEAN int
  8695. #define TRUE    1
  8696. #define FALSE   0
  8697.  
  8698. #define ESC 27
  8699. #define ENTER '\r'
  8700.  
  8701. #define NUMMAXCARACTERES 255
  8702.  
  8703. void main (void)
  8704. {
  8705.   BOOLEAN salir = FALSE;
  8706.   char nombre_fichero_entrada[NUMMAXCARACTERES],
  8707.        nombre_fichero_salida[NUMMAXCARACTERES];
  8708.   int dfe, dfs; /* descriptores de los ficheros de entrada y de salida resp. */
  8709.   char ch;
  8710.  
  8711.   while (! salir)
  8712.     {
  8713.       puts ("\n\nCOPIAR FICHERO CON EL SISTEMA DE FICHEROS DE BAJO NIVEL:\n");
  8714.       printf ("Introduce nombre de fichero de entrada (ENTER o CON para teclado): ");
  8715.       gets (nombre_fichero_entrada);
  8716.       if (*nombre_fichero_entrada == '\0')
  8717.         strcpy (nombre_fichero_entrada, "con");
  8718.       printf ("Introduce nombre de fichero de salida (ENTER o CON para pantalla): ");
  8719.       gets (nombre_fichero_salida);
  8720.       if (*nombre_fichero_salida == '\0')
  8721.         strcpy (nombre_fichero_salida, "con");
  8722.  
  8723.       if ((dfe = open (nombre_fichero_entrada, O_RDONLY)) == -1)
  8724.         printf ("\nERROR: No es posible abrir el fichero de entrada %s.\n",
  8725.                 nombre_fichero_entrada);
  8726.       else if ((dfs = open (nombre_fichero_salida,
  8727.                O_WRONLY | O_CREAT, S_IREAD | S_IWRITE)) == -1)
  8728.                /* si no se pone el último argumento, el atributo de fichero
  8729.                   es sólo lectura, y no se puede borrar de disco sin cambiar
  8730.                   el modo */
  8731.         {
  8732.           printf ("\nERROR: No es posible abrir el fichero de salida %s.\n",
  8733.                   nombre_fichero_salida);
  8734.           close (dfe);
  8735.         }
  8736.       else
  8737.         {
  8738.           char buf[BUFSIZ];
  8739.           int n;
  8740.  
  8741.           while ((n = read (dfe, buf, BUFSIZ)) > 0)
  8742.             write (dfs, buf, n);
  8743.  
  8744.           close (dfe);
  8745.           close (dfs);
  8746.         }
  8747.  
  8748.       printf ("\n\n¿Desea copiar otro fichero (S o ENTER: Sí; N o ESC: No)? ");
  8749.       do
  8750.         {
  8751.           ch = getch ();
  8752.         } while (ch != ENTER && toupper (ch) != 'S' &&
  8753.                  ch != ESC && toupper (ch) != 'N');
  8754.       salir = ch == ESC || toupper (ch) == 'N';
  8755.     }
  8756. }
  8757. ende
  8758. endt
  8759. begine " COPIAR FICH LINEA A LINEA ESCRIBIENDO NUMEROS DE LINEA "
  8760. /*
  8761.   Copia un fichero en otro línea a línea escribiendo el número de línea al
  8762.   principio de cada línea.
  8763.  
  8764.   El fichero de entrada (de lectura) no puede tener más de NUMMAXCARACTERES
  8765.   caracteres por línea. Esto se puede solucionar leyendo carácter a carácter.
  8766. */
  8767.  
  8768. #include <stdio.h>  /* printf (), gets (), fprintf (), stderr, EOF, fopen (),
  8769.                        fclose (), fgets (), NULL */
  8770. #include <conio.h>  /* getch () */
  8771. #include <string.h> /* strcpy () */
  8772. #include <ctype.h>  /* toupper () */
  8773.  
  8774. #define BOOLEAN int
  8775. #define TRUE    1
  8776. #define FALSE   0
  8777.  
  8778. #define ESC 27
  8779. #define ENTER '\r'
  8780.  
  8781. #define NUMMAXCARACTERES 255
  8782.  
  8783. void main (void)
  8784. {
  8785.   BOOLEAN salir = FALSE;
  8786.   char nombre_fichero_entrada[NUMMAXCARACTERES],
  8787.        nombre_fichero_salida[NUMMAXCARACTERES];
  8788.   FILE *pfe, *pfs; /* punteros a fichero de entrada y fichero de salida resp. */
  8789.   char ch;
  8790.  
  8791.   while (! salir)
  8792.     {
  8793.       puts ("\n\nCOPIAR FICHERO LINEA A LINEA:\n");
  8794.       printf ("Introduce nombre de fichero de entrada (ENTER o CON para teclado): ");
  8795.       gets (nombre_fichero_entrada);
  8796.       if (*nombre_fichero_entrada == '\0')
  8797.         strcpy (nombre_fichero_entrada, "con");
  8798.       printf ("Introduce nombre de fichero de salida (ENTER o CON para pantalla): ");
  8799.       gets (nombre_fichero_salida);
  8800.       if (*nombre_fichero_salida == '\0')
  8801.         strcpy (nombre_fichero_salida, "con");
  8802.  
  8803.       if ((pfe = fopen (nombre_fichero_entrada, "r")) == NULL)
  8804.         fprintf (stderr, "\nERROR: No es posible abrir el fichero de entrada %s.\n",
  8805.                  nombre_fichero_entrada);
  8806.       else if ((pfs = fopen (nombre_fichero_salida, "w")) == NULL)
  8807.         {
  8808.           fprintf (stderr, "\nERROR: No es posible abrir el fichero de salida %s.\n",
  8809.                   nombre_fichero_salida);
  8810.           fclose (pfe);
  8811.         }
  8812.       else
  8813.         {
  8814.           int num_linea = 0;
  8815.           char linea[NUMMAXCARACTERES];
  8816.  
  8817.           while (fgets (linea, NUMMAXCARACTERES, pfe))
  8818.             fprintf (pfs, "%3d %s", ++num_linea, linea);
  8819.  
  8820.           fclose (pfe);
  8821.           fclose (pfs);
  8822.         }
  8823.  
  8824.       printf ("\n\n¿Desea copiar otro fichero (S o ENTER: Sí; N o ESC: No)? ");
  8825.       do
  8826.         {
  8827.           ch = getch ();
  8828.         } while (ch != ENTER && toupper (ch) != 'S' &&
  8829.                  ch != ESC && toupper (ch) != 'N');
  8830.       salir = ch == ESC || toupper (ch) == 'N';
  8831.     }
  8832. }
  8833. ende
  8834. begine " COPIAR FICHERO SELECTIVAMENTE "
  8835. /*
  8836.   Copia las líneas de un fichero que contenga un determinado texto en
  8837.   otro fichero. Las líneas que no contengan el texto dado no son copiadas
  8838.   en el fichero de destino.
  8839.  
  8840.   El fichero de entrada (de lectura) no puede tener más de NUMMAXCARACTERES
  8841.   caracteres por línea.
  8842.  
  8843.   En ese programa aparece una función nueva cuyo prototipo está en el
  8844.   fichero string.h:
  8845.  
  8846.     char *strstr (const char *s1, const char *s2);
  8847.  
  8848.   Esta función devuelve un puntero al elemento en s1 que contiene s2
  8849.   (apunta a s2 en s1), o NULL si s2 no aparece en s1. En nuestro programa
  8850.   lo que nos interesa es que devuelve un valor distinto de cero (dirección)
  8851.   si s2 está contenido en s1 y un valor de 0 (NULL) en caso de que s2 no
  8852.   esté en s1.
  8853. */
  8854.  
  8855. #include <stdio.h>  /* printf (), gets (), fprintf (), stderr, EOF, fopen (),
  8856.                        fclose (), fgets (), fputs (), NULL */
  8857. #include <conio.h>  /* getch () */
  8858. #include <string.h> /* strcpy (), strstr () */
  8859. #include <ctype.h>  /* toupper () */
  8860.  
  8861. #define BOOLEAN int
  8862. #define TRUE    1
  8863. #define FALSE   0
  8864.  
  8865. #define ESC 27
  8866. #define ENTER '\r'
  8867.  
  8868. #define NUMMAXCARACTERES 255
  8869.  
  8870. void main (void)
  8871. {
  8872.   BOOLEAN salir = FALSE;
  8873.   char nombre_fichero_entrada[NUMMAXCARACTERES],
  8874.        nombre_fichero_salida[NUMMAXCARACTERES];
  8875.   FILE *pfe, *pfs; /* punteros a fichero de entrada y fichero de salida resp. */
  8876.   char ch;
  8877.  
  8878.   while (! salir)
  8879.     {
  8880.       puts ("\n\nCOPIAR LINEAS SELECTIVAMENTE:\n");
  8881.       printf ("Introduce nombre de fichero de entrada (ENTER o CON para teclado): ");
  8882.       gets (nombre_fichero_entrada);
  8883.       if (*nombre_fichero_entrada == '\0')
  8884.         strcpy (nombre_fichero_entrada, "con");
  8885.       printf ("Introduce nombre de fichero de salida (ENTER o CON para pantalla): ");
  8886.       gets (nombre_fichero_salida);
  8887.       if (*nombre_fichero_salida == '\0')
  8888.         strcpy (nombre_fichero_salida, "con");
  8889.  
  8890.       if ((pfe = fopen (nombre_fichero_entrada, "r")) == NULL)
  8891.         fprintf (stderr, "\nERROR: No es posible abrir el fichero de entrada %s.\n",
  8892.                  nombre_fichero_entrada);
  8893.       else if ((pfs = fopen (nombre_fichero_salida, "w")) == NULL)
  8894.         {
  8895.           fprintf (stderr, "\nERROR: No es posible abrir el fichero de salida %s.\n",
  8896.                   nombre_fichero_salida);
  8897.           fclose (pfe);
  8898.         }
  8899.       else
  8900.         {
  8901.           char linea[NUMMAXCARACTERES], texto[NUMMAXCARACTERES];
  8902.  
  8903.           printf ("Introduce texto: ");
  8904.           gets (texto);
  8905.  
  8906.           while (fgets (linea, NUMMAXCARACTERES, pfe))
  8907.             if (strstr (linea, texto))
  8908.               fputs (linea, pfs);
  8909.  
  8910.           fclose (pfe);
  8911.           fclose (pfs);
  8912.         }
  8913.  
  8914.       printf ("\n\n¿Desea copiar otro fichero (S o ENTER: Sí; N o ESC: No)? ");
  8915.       do
  8916.         {
  8917.           ch = getch ();
  8918.         } while (ch != ENTER && toupper (ch) != 'S' &&
  8919.                  ch != ESC && toupper (ch) != 'N');
  8920.       salir = ch == ESC || toupper (ch) == 'N';
  8921.     }
  8922. }
  8923. ende
  8924. begine " COMPARAR DOS FICHEROS "
  8925. /*
  8926.   Este programa compara dos ficheros.
  8927. */
  8928.  
  8929. #include <stdio.h>  /* printf (), gets (), fprintf (), stderr, EOF, fopen (),
  8930.                        fclose (), fgets (), fputs (), NULL */
  8931. #include <conio.h>  /* getch () */
  8932. #include <string.h> /* strcpy (), strstr () */
  8933. #include <ctype.h>  /* toupper () */
  8934.  
  8935. #define BOOLEAN int
  8936. #define TRUE    1
  8937. #define FALSE   0
  8938.  
  8939. #define ESC 27
  8940. #define ENTER '\r'
  8941.  
  8942. #define NUMMAXCARACTERES 255
  8943.  
  8944. void main (void)
  8945. {
  8946.   BOOLEAN salir = FALSE;
  8947.   char nombre_fichero_1[NUMMAXCARACTERES],
  8948.        nombre_fichero_2[NUMMAXCARACTERES];
  8949.   FILE *pf1, *pf2; /* punteros a fichero de entrada y fichero de salida resp. */
  8950.   char ch;
  8951.  
  8952.   while (! salir)
  8953.     {
  8954.       puts ("\n\nCOMPARAR DOS FICHEROS:\n");
  8955.       printf ("Introduce nombre de primer fichero (ENTER o CON para teclado): ");
  8956.       gets (nombre_fichero_1);
  8957.       if (*nombre_fichero_1 == '\0')
  8958.         strcpy (nombre_fichero_1, "con");
  8959.       printf ("Introduce nombre de segundo fichero (ENTER o CON para teclado): ");
  8960.       gets (nombre_fichero_2);
  8961.       if (*nombre_fichero_2 == '\0')
  8962.         strcpy (nombre_fichero_2, "con");
  8963.  
  8964.       if ((pf1 = fopen (nombre_fichero_1, "r")) == NULL)
  8965.         fprintf (stderr, "\nERROR: No es posible abrir el fichero %s.\n",
  8966.                  nombre_fichero_1);
  8967.       else if ((pf2 = fopen (nombre_fichero_2, "r")) == NULL)
  8968.         {
  8969.           fprintf (stderr, "\nERROR: No es posible abrir el fichero %s.\n",
  8970.                   nombre_fichero_2);
  8971.           fclose (pf1);
  8972.         }
  8973.       else
  8974.         {
  8975.           int ch1, ch2;
  8976.  
  8977.           while ((ch1 = fgetc (pf1)) == (ch2 = fgetc (pf2)) && ch1 != EOF)
  8978.             ;
  8979.  
  8980.           printf ("\nLos dos ficheros comparados son %s.\n",
  8981.                   feof (pf1) && feof (pf2) ? "iguales" : "distintos");
  8982.  
  8983.           fclose (pf1);
  8984.           fclose (pf2);
  8985.         }
  8986.  
  8987.       printf ("\n\n¿Desea comparar otros dos ficheros (S o ENTER: Sí; N o ESC: No)? ");
  8988.       do
  8989.         {
  8990.           ch = getch ();
  8991.         } while (ch != ENTER && toupper (ch) != 'S' &&
  8992.                  ch != ESC && toupper (ch) != 'N');
  8993.       salir = ch == ESC || toupper (ch) == 'N';
  8994.     }
  8995. }
  8996. ende
  8997. begine " COPIAR FICHERO CON SUSTITUCIONES "
  8998. /*
  8999.   Copia un fichero en otro pero sustituyendo un determinado texto por otro
  9000.   nuevo texto.
  9001.  
  9002.   El fichero de entrada (de lectura) no puede tener más de NUMMAXCARACTERES
  9003.   caracteres por línea.
  9004.  
  9005.   En ese programa aparece una función nueva cuyo prototipo está en el
  9006.   fichero string.h:
  9007.  
  9008.     char *strstr (const char *s1, const char *s2);
  9009.  
  9010.   Esta función devuelve un puntero al elemento en s1 que contiene s2
  9011.   (apunta a s2 en s1), o NULL si s2 no aparece en s1. En nuestro programa
  9012.   lo que nos interesa es que devuelve un valor distinto de cero (dirección)
  9013.   si s2 está contenido en s1 y un valor de 0 (NULL) en caso de que s2 no
  9014.   esté en s1.
  9015. */
  9016.  
  9017. #include <stdio.h>  /* printf (), gets (), fprintf (), stderr, EOF, fopen (),
  9018.                        fclose (), fgets (), fputs (), NULL */
  9019. #include <conio.h>  /* getch () */
  9020. #include <string.h> /* strcpy (), strstr () */
  9021. #include <ctype.h>  /* toupper () */
  9022.  
  9023. #define BOOLEAN int
  9024. #define TRUE    1
  9025. #define FALSE   0
  9026.  
  9027. #define ESC 27
  9028. #define ENTER '\r'
  9029.  
  9030. #define NUMMAXCARACTERES 255
  9031.  
  9032. void main (void)
  9033. {
  9034.   BOOLEAN salir = FALSE;
  9035.   char nombre_fichero_entrada[NUMMAXCARACTERES],
  9036.        nombre_fichero_salida[NUMMAXCARACTERES];
  9037.   FILE *pfe, *pfs; /* punteros a fichero de entrada y fichero de salida resp. */
  9038.   char ch;
  9039.  
  9040.   while (! salir)
  9041.     {
  9042.       puts ("\n\nCOPIAR LINEAS SELECTIVAMENTE:\n");
  9043.       printf ("Introduce nombre de fichero de entrada (ENTER o CON para teclado): ");
  9044.       gets (nombre_fichero_entrada);
  9045.       if (*nombre_fichero_entrada == '\0')
  9046.         strcpy (nombre_fichero_entrada, "con");
  9047.       printf ("Introduce nombre de fichero de salida (ENTER o CON para pantalla): ");
  9048.       gets (nombre_fichero_salida);
  9049.       if (*nombre_fichero_salida == '\0')
  9050.         strcpy (nombre_fichero_salida, "con");
  9051.  
  9052.       if ((pfe = fopen (nombre_fichero_entrada, "r")) == NULL)
  9053.         fprintf (stderr, "\nERROR: No es posible abrir el fichero de entrada %s.\n",
  9054.                  nombre_fichero_entrada);
  9055.       else if ((pfs = fopen (nombre_fichero_salida, "w")) == NULL)
  9056.         {
  9057.           fprintf (stderr, "\nERROR: No es posible abrir el fichero de salida %s.\n",
  9058.                   nombre_fichero_salida);
  9059.           fclose (pfe);
  9060.         }
  9061.       else
  9062.         {
  9063.           char linea[NUMMAXCARACTERES], texto_viejo[NUMMAXCARACTERES],
  9064.                texto_nuevo[NUMMAXCARACTERES], *pc1, *pc2, longitud_texto_viejo;
  9065.  
  9066.           printf ("Introduce texto viejo: ");
  9067.           gets (texto_viejo);
  9068.           printf ("Introduce texto nuevo: ");
  9069.           gets (texto_nuevo);
  9070.  
  9071.           longitud_texto_viejo = strlen (texto_viejo);
  9072.           while (fgets (linea, NUMMAXCARACTERES, pfe))
  9073.             {
  9074.               for (pc1 = linea; (pc2 = strstr (pc1, texto_viejo)) != NULL;
  9075.                    pc1 = pc2 + longitud_texto_viejo)
  9076.                 {
  9077.                   *pc2 = 0;
  9078.                   fprintf (pfs, "%s%s", pc1, texto_nuevo);
  9079.                 }
  9080.               fputs (pc1, pfs);
  9081.             }
  9082.  
  9083.           fclose (pfe);
  9084.           fclose (pfs);
  9085.         }
  9086.  
  9087.       printf ("\n\n¿Desea copiar otro fichero (S o ENTER: Sí; N o ESC: No)? ");
  9088.       do
  9089.         {
  9090.           ch = getch ();
  9091.         } while (ch != ENTER && toupper (ch) != 'S' &&
  9092.                  ch != ESC && toupper (ch) != 'N');
  9093.       salir = ch == ESC || toupper (ch) == 'N';
  9094.     }
  9095. }
  9096. ende
  9097. begine " ORDENAR FICHERO "
  9098. /*
  9099.   Este programa lee las líneas de un fichero de entrada (que puede ser la
  9100.   consola) y escribe las líneas ordenadas en un fichero de salida (que puede
  9101.   ser la consola).
  9102.  
  9103.   La estructura del árbol y las funciones relacionadas con él están cogidas
  9104.   del segundo ejemplo de la lección 8. Ver el código fuente de aquel ejemplo
  9105.   si no se entiende algún función de árboles que en este programa.
  9106. */
  9107.  
  9108. #include <stdio.h>  /* printf (), gets (), FILE, fopen (), fclose (), NULL,
  9109.                        fprintf (), fgets () */
  9110. #include <stdlib.h> /* exit () */
  9111. #include <alloc.h>  /* malloc (), free () */
  9112. #include <conio.h>  /* getch () */
  9113. #include <string.h> /* strcmp (), strcpy () */
  9114. #include <ctype.h>  /* toupper () */
  9115.  
  9116. #define BOOLEAN int
  9117. #define TRUE    1
  9118. #define FALSE   0
  9119.  
  9120. #define ESC 27
  9121. #define ENTER '\r' /* no vale '\n' */
  9122.  
  9123. #define NUMMAXCARACTERES 255
  9124.  
  9125. struct nodo_arbol
  9126.  {
  9127.    char linea[NUMMAXCARACTERES];
  9128.    struct nodo_arbol *pizq, *pder;
  9129.  };
  9130.  
  9131. void inicializar_arbol (struct nodo_arbol **parb);
  9132. void hacer_nodo_arbol (struct nodo_arbol **pa);
  9133. void insertar_en_arbol (struct nodo_arbol **parb, char *lin);
  9134. void imprimir_arbol (struct nodo_arbol *parb, FILE *pfichsal);
  9135. void liberar_arbol (struct nodo_arbol **parb);
  9136.  
  9137. void prlec9_7 (void)
  9138. {
  9139.   BOOLEAN salir = FALSE;
  9140.   char nombre_fichero_entrada[NUMMAXCARACTERES],
  9141.        nombre_fichero_salida[NUMMAXCARACTERES],
  9142.        linea_fichero[NUMMAXCARACTERES];
  9143.   FILE *pfe, *pfs; /* punteros a fichero de entrada y fichero de salida resp. */
  9144.   char ch;
  9145.   struct nodo_arbol *parbol;
  9146.  
  9147.   while (! salir)
  9148.     {
  9149.       puts ("\n\nORDENACION DE FICHEROS.\n");
  9150.       printf ("Introduce nombre de fichero de entrada (ENTER o CON para teclado): ");
  9151.       gets (nombre_fichero_entrada);
  9152.       if (*nombre_fichero_entrada == '\0')
  9153.         strcpy (nombre_fichero_entrada, "con");
  9154.       printf ("Introduce nombre de fichero de salida (ENTER o CON para pantalla): ");
  9155.       gets (nombre_fichero_salida);
  9156.       if (*nombre_fichero_salida == '\0')
  9157.         strcpy (nombre_fichero_salida, "con");
  9158.  
  9159.       if ((pfe = fopen (nombre_fichero_entrada, "r")) == NULL)
  9160.         printf ("\nERROR: No es posible abrir el fichero de entrada %s.\n",
  9161.                   nombre_fichero_entrada);
  9162.       else if ((pfs = fopen (nombre_fichero_salida, "w")) == NULL)
  9163.         {
  9164.           printf ("\nERROR: No es posible abrir el fichero de salida %s.\n",
  9165.                     nombre_fichero_salida);
  9166.           fclose (pfe);
  9167.         }
  9168.       else
  9169.         {
  9170.           inicializar_arbol (&parbol);
  9171.           while (fgets (linea_fichero, sizeof (linea_fichero), pfe) != NULL)
  9172.             insertar_en_arbol (&parbol, linea_fichero);
  9173.  
  9174.           imprimir_arbol (parbol, pfs);
  9175.  
  9176.           fclose (pfe);
  9177.           fclose (pfs);
  9178.  
  9179.           liberar_arbol (&parbol);
  9180.         }
  9181.  
  9182.       printf ("\n\n¿Desea ordenar otro fichero (S o ENTER: Sí; N o ESC: No)? ");
  9183.       do
  9184.         {
  9185.           ch = getch ();
  9186.         } while (ch != ENTER && toupper (ch) != 'S' &&
  9187.                  ch != ESC && toupper (ch) != 'N');
  9188.       salir = ch == ESC || toupper (ch) == 'N';
  9189.     }
  9190. }
  9191.  
  9192. void inicializar_arbol (struct nodo_arbol **parb)
  9193. {
  9194.   *parb = NULL;
  9195. }
  9196.  
  9197. void hacer_nodo_arbol (struct nodo_arbol **pa)
  9198. {
  9199.   if ((*pa = (struct nodo_arbol *) malloc (sizeof (struct nodo_arbol))) == NULL)
  9200.     {
  9201.       printf ("\nERROR: Memoria insuficiente.");
  9202.       exit (1);
  9203.     }
  9204. }
  9205.  
  9206. void insertar_en_arbol (struct nodo_arbol **parb, char *lin)
  9207. {
  9208.   struct nodo_arbol *p, *pant, *pa;
  9209.  
  9210.   hacer_nodo_arbol (&pa);
  9211.   strcpy (pa->linea, lin);
  9212.   pa->pizq = pa->pder = NULL;
  9213.  
  9214.   p = *parb;
  9215.   pant = NULL;
  9216.   while (p != NULL)
  9217.     {
  9218.       pant = p;
  9219.       if (strcmp (p->linea, lin) > 0)
  9220.         p = p->pizq;
  9221.       else
  9222.         p = p->pder;
  9223.     }
  9224.  
  9225.   if (pant == NULL)
  9226.     *parb = pa;
  9227.   else if (strcmp (pant->linea, lin) > 0)
  9228.     pant->pizq = pa;
  9229.   else
  9230.     pant->pder = pa;
  9231. }
  9232.  
  9233. void imprimir_arbol (struct nodo_arbol *parb, FILE *pfichsal)
  9234. {
  9235.   if (parb != NULL)
  9236.     {
  9237.       imprimir_arbol (parb->pizq, pfichsal);
  9238.       fprintf (pfichsal, parb->linea);
  9239.       imprimir_arbol (parb->pder, pfichsal);
  9240.     }
  9241. }
  9242.  
  9243. void liberar_arbol (struct nodo_arbol **parb)
  9244. {
  9245.   if (*parb != NULL)
  9246.     {
  9247.       liberar_arbol (&(*parb)->pizq);
  9248.       liberar_arbol (&(*parb)->pder);
  9249.       free (*parb);
  9250.     }
  9251. }
  9252. ende
  9253. end lección 9
  9254.  
  9255. ; LECCION 10
  9256. begin
  9257. begine " CONSTANTES DEFINIDAS EN FICHERO LIMITS.H "
  9258. /*
  9259.   Este programa imprime los valores de todas las constantes definidas en
  9260.   el fichero limits.h.
  9261. */
  9262.  
  9263. #include <stdio.h>
  9264. #include <limits.h>
  9265. #include <conio.h>
  9266.  
  9267. void main (void)
  9268. {
  9269.   puts ("MACROS DECLARADAS EN EL FICHERO DE CABECERA LIMITS.H:");
  9270.   printf ("\nCHAR_BIT: %d", CHAR_BIT);
  9271.   printf ("\nCHAR_MAX: %c (%d)", CHAR_MAX, CHAR_MAX);
  9272.   printf ("\nCHAR_MIN: %c (%d)", CHAR_MIN, CHAR_MIN);
  9273.   printf ("\nSCHAR_MAX: %c (%d)", SCHAR_MAX, SCHAR_MAX);
  9274.   printf ("\nSCHAR_MIN: %c (%d)", SCHAR_MIN, SCHAR_MIN);
  9275.   printf ("\nUCHAR_MAX: %c (%d)", UCHAR_MAX, UCHAR_MAX);
  9276.   printf ("\nSHRT_MAX: %hd", SHRT_MAX);
  9277.   printf ("\nSHRT_MIN: %hd", SHRT_MIN);
  9278.   printf ("\nUSHRT_MAX: %u", USHRT_MAX);
  9279.   printf ("\nINT_MAX: %d", INT_MAX);
  9280.   printf ("\nINT_MIN: %d", INT_MIN);
  9281.   printf ("\nUINT_MAX: %u", UINT_MAX);
  9282.   printf ("\nLONG_MAX: %ld", LONG_MAX);
  9283.   printf ("\nLONG_MIN: %ld", LONG_MIN);
  9284.   printf ("\nULONG_MAX: %lu", ULONG_MAX);
  9285.   getch ();
  9286. }
  9287. ende
  9288. begine " MACROS Y FUNCIONES DECLARADAS EN FICHERO CTYPE.H "
  9289. /*
  9290.   Este programa muestra la utilización y funcionamiento de todas las
  9291.   macros y funciones que se encuentran declaradas en el fichero ctype.h.
  9292.  
  9293.   Recordar que las teclas especiales como las teclas de función generan
  9294.   dos caracteres: carácter 0 y carácter de exploración.
  9295.  
  9296.   Si este programa se compila en un compilador de Turbo C las tres líneas
  9297.   entre las directivas #ifdef-#endif serán compiladas, si se compila en
  9298.   cualquier otro compilador de C, estas tres líneas no serán compiladas.
  9299. */
  9300.  
  9301. #include <ctype.h> /* macros y funciones de caracteres */
  9302. #include <stdio.h> /* printf (), puts ()               */
  9303. #include <conio.h> /* getch (), getche ()              */
  9304.  
  9305. void main (void)
  9306. {
  9307.   char ch;
  9308.  
  9309.   puts ("PROGRAMA PARA PROBAR MACROS Y FUNCIONES DE CTYPE.H");
  9310.  
  9311.   do
  9312.     {
  9313.       printf ("\n\nPulsa una tecla (ESC para salir): ");
  9314.       ch = getche ();
  9315.       printf ("\n");
  9316.       printf ("\nisalnum(%c) devuelve %d.", ch, isalnum(ch));
  9317.       printf ("\nisalpha(%c) devuelve %d.", ch, isalpha(ch));
  9318.       printf ("\nisdigit(%c) devuelve %d.", ch, isdigit(ch));
  9319.       printf ("\niscntrl(%c) devuelve %d.", ch, iscntrl(ch));
  9320.       printf ("\nisascii(%c) devuelve %d.", ch, isascii(ch));
  9321.       printf ("\nisprint(%c) devuelve %d.", ch, isprint(ch));
  9322.       printf ("\nisgraph(%c) devuelve %d.", ch, isgraph(ch));
  9323.       printf ("\nislower(%c) devuelve %d.", ch, islower(ch));
  9324.       printf ("\nisupper(%c) devuelve %d.", ch, isupper(ch));
  9325.       printf ("\nispunct(%c) devuelve %d.", ch, ispunct(ch));
  9326.       printf ("\nisspace(%c) devuelve %d.", ch, isspace(ch));
  9327.       printf ("\nisxdigit(%c) devuelve %d.", ch, isxdigit(ch));
  9328.       #ifdef __TURBOC__
  9329.         printf ("\n_toupper(%c) devuelve %c.", ch, _toupper(ch));
  9330.         printf ("\n_tolower(%c) devuelve %c.", ch, _tolower(ch));
  9331.         printf ("\ntoascii(%c) devuelve %c.", ch, toascii(ch));
  9332.       #endif
  9333.       printf ("\ntoupper(%c) devuelve %c.", ch, toupper(ch));
  9334.       printf ("\ntolower(%c) devuelve %c.", ch, tolower(ch));
  9335.     } while (ch != 27); /* mientras ch sea distinto de escape */
  9336.  
  9337.   printf ("\n\nPulsa cualquier tecla para finalizar. ");
  9338.   getch ();
  9339. }
  9340. ende
  9341. begine " COMPRESION/DESCOMPRESION DE FICHEROS "
  9342. /*
  9343.  
  9344.   Este programa comprime y descomprime ficheros utilizando el algoritmo
  9345.   de Fuffman.
  9346.  
  9347.  
  9348.   INTRODUCCION TEORICA:
  9349.  
  9350.   Considera el problema siguiente. Supongamos que tenemos un alfabeto de n
  9351.   símbolos y un mensaje muy largo consistente de los símbolos de ese alfa-
  9352.   beto. Nosotros queremos codificar este mensaje como una hilera muy larga
  9353.   de bits (definimos un bit como 0 ó 1) asignando un código de hileras de
  9354.   bit a cada símbolo del alfabeto y concatenando los códigos individuales
  9355.   de los símbolos que hacen el mensaje, para producir una forma codificada
  9356.   del mensaje. Por ejemplo, supongamos que el alfabeto consiste de los cua-
  9357.   tro símbolos A, B, C, D y los códigos asignados a estos símbolos es como
  9358.   sigue:
  9359.                        -------------------------
  9360.                         Símbolo          Código
  9361.                            A               010
  9362.                            B               100
  9363.                            C               000
  9364.                            D               111
  9365.                        -------------------------
  9366.  
  9367.   El mensaje ABACCDA sería codificado como 010100010000000111010. Sin em-
  9368.   bargo esta codificación sería ineficiente puesto que se utilizan 3 bits
  9369.   para cada número, entonces se requerirían 21 bits para codificar el men-
  9370.   saje en forma total. Supongamos que se utiliza un código de 2 bit para
  9371.   cada símbolo en la forma que sigue:
  9372.  
  9373.                        -------------------------
  9374.                         Símbolo          Código
  9375.                            A               10
  9376.                            B               01
  9377.                            C               10
  9378.                            D               11
  9379.                        -------------------------
  9380.  
  9381.   En este caso el código para el mensaje sería 00010010101100, el cual re-
  9382.   quiere únicamente 14 bits. Deseamos encontrar un código que minimiza la
  9383.   longitud del mensaje total codificado.
  9384.  
  9385.   Reexaminemos el ejemplo anterior. Cada una de las letras B y D aparecen
  9386.   sólo una vez en el mensaje, mientras que la letra A aparece tres veces.
  9387.   Es decir, que si se escoge un código en el cual a la letra A se le asigna
  9388.   la hilera de bits más corta comparado con las letras B y D, la longitud
  9389.   del mensaje codificado será menor. Esto se debe a que el código más corto
  9390.   (que representa la letra A) aparece más frecuentemente que el código más
  9391.   largo. En realidad podríamos asignar los siguientes códigos:
  9392.  
  9393.                        -------------------------
  9394.                         Símbolo          Código
  9395.                            A               0
  9396.                            B               110
  9397.                            C               10
  9398.                            D               111
  9399.                        -------------------------
  9400.  
  9401.   Utilizando este código, el mensaje ABACCDA quedaría codificado como 01100
  9402.   10101110, el cual requiere únicamente 13 bits. En mensajes muy largos que
  9403.   contienen símbolos que aparecen muy rara vez, habría un ahorro muy sustan-
  9404.   cial. Observa que el código para un símbolo no debe ser un prefijo del có-
  9405.   digo para otro. Esto es cierto si la decodificación se hace de izquierda a
  9406.   derecha. Si el código para un símbolo x, c(x), fuera el prefijo del código
  9407.   para un símbolo y, c(y), entonces cuando se encuentre c(x), no estaría muy
  9408.   claro si c(x) representa al símbolo x o si es la primera parte de c(y). En
  9409.   nuestro ejemplo la hilera de bits es recorrida de izquierda a derecha. Si
  9410.   se encuentra un 0 como el primer bit, se tendría como símbolo la letra A;
  9411.   en caso contrario se tendría B, C o D y se analizaría el bit siguiente. Si
  9412.   el segundo bit es un 0, entonces tenemos el símbolo C; de lo contrario debe
  9413.   ser B o D y se analizaría el tercer bit. Si el tercer bit es un 0, el sím-
  9414.   bolo es una B; si es 1, el símbolo es una D. Tan pronto como se ha encon-
  9415.   trado el primer símbolo, el proceso se repite partiendo nuevamente con el
  9416.   bit siguiente para encontrar el segundo símbolo.
  9417.  
  9418.   Lo anterior sugiere que se debe encontrar un método de codificación en for-
  9419.   ma óptima de acuerdo a la frecuencia de ocurrencia de cada símbolo en el
  9420.   mensaje. Primero encontrar los dos símbolos que aparecen menos frecuente-
  9421.   mente. En nuestro ejemplo estos son B y D. El último bit de sus códigos
  9422.   muestra la diferencia entre ellos: 0 para el caso de B, y 1 para el caso
  9423.   de D. Combina estos dos símbolos en un solo símbolo BD, cuyo código repre-
  9424.   sente qué símbolo es, ya sea B o D. La frecuencia de ocurrencia de este
  9425.   nuevo símbolo es la suma de las frecuencias de sus dos símbolos constitu-
  9426.   yentes. Es decir, la frecuencia de BD, es 2. En este caso tendríamos ahora
  9427.   tres símbolos: A (con frecuencia 3), C (con frecuencia 2), y BD (con fre-
  9428.   cuencia 2). De nuevo escoje los dos símbolos con menor frecuencia: C y BD.
  9429.   El último bit de sus códigos los diferencia entre sí: 0 para C y 1 para BD.
  9430.   Los dos símbolos son ahora combinados en un solo símbolo CBD con frecuencia
  9431.   4. Ahora tendremos dos símbolos únicamente: A y CBD. Estos son combinados
  9432.   en un solo símbolo ACBD. El último bit de los códigos para A y CBD muestra
  9433.   la diferencia entre ellos: 0 para A y 1 para CBD.
  9434.  
  9435.   El símbolo ACBD contiene ahora el alfabeto en forma completa; a éste se le
  9436.   asigna la hilera de bits nulo de longitud 0 como código. Esto significa que
  9437.   al comienzo de la decodificación antes de que cualquier bit haya sido exa-
  9438.   minado, estamos seguros que cualquier símbolo está contenido en ACBD. Los
  9439.   dos símbolos que contienen ACBD (A y CBD) se le asignan los códigos 0 y 1,
  9440.   respectivamente. Si se encuentra un 0, el símbolo codificado es una A; si
  9441.   se encuentra 1, es C, B o D. Igualmente los símbolos que constituyen CBD
  9442.   (C y BD) se le asignan los códigos 10 y 11, respectivamente. El primer bit
  9443.   indica que el símbolo es uno de los constituyentes de CBD y el segundo bit
  9444.   indica que es C o BD. El símbolo que compone BD (B y D) se le asigna los
  9445.   códigos 110 y 111. Mediante este proceso, los símbolos que aparecen frecuen-
  9446.   temente en el mensaje quedan con los códigos más cortos que los símbolos que
  9447.   aparecen con menos frecuencia.
  9448.  
  9449.   La acción de combinar dos símbolos en uno solo sugiere el uso de un árbol
  9450.   binario. Cada nodo sin hojas del árbol representa un símbolo y las hojas re-
  9451.   presentan el símbolo del alfabeto original. La siguiente figura muestra el
  9452.   árbol binario construido utilizando el ejemplo anterior. Cada nodo en la
  9453.   ilustración contiene un símbolo y su frecuencia.
  9454.  
  9455.                                     ACBD.7
  9456.                                     /   \
  9457.                                   A.3  CBD.4
  9458.                                        /   \
  9459.                                      C.2   BD.2
  9460.                                            /  \
  9461.                                          B.1  D.1
  9462.  
  9463.   La figura que viene a continuación demuestra el árbol binario construido
  9464.   por este método para el alfabeto de la tabla de frecuencias adjunta.
  9465.  
  9466.                                    IHFBDEGCA.91
  9467.                                    /          \
  9468.                                  /              \
  9469.                              IHFBD.38          EGCA.53
  9470.                             /      \           /     \
  9471.                           /          \       /         \
  9472.                         I.15      HFBD.23  E.25       GCA.28
  9473.                                   /    \               / \
  9474.                                 /        \           /     \
  9475.                              HFB.11     D.12       GC.13  A.15
  9476.                              /  \                   / \
  9477.                            /      \               /     \
  9478.                          HF.5     B.6           G.6     C.7
  9479.                          / \
  9480.                        /     \
  9481.                      H.1     F.4
  9482.  
  9483.  Símbolo Frecuencia Código │ Símbolo Frecuencia Código │ Símbolo Frecuencia Código
  9484. ───────────────────────────┼───────────────────────────┼───────────────────────────
  9485.     A        15       111  │    D        12       011  │    G         6      1100
  9486.     B         6      0101  │    E        25        10  │    H         1     01000
  9487.     C         7      1101  │    F         4     01001  │    I        15        00
  9488.  
  9489.   Estos árboles son denominados árboles de Huffman debido al descubridor de
  9490.   este método de codificación.
  9491.  
  9492.   Una vez que el árbol de Huffman se ha construido, el código de cualquier
  9493.   símbolo en el alfabeto se puede determinar empezando con la hoja que re-
  9494.   presenta el símbolo y subiendo hasta la raíz. El código es inicializado
  9495.   en cero. Cada vez que se asciende en una ramificación de la izquierda se
  9496.   agrega un 0 a la izquierda en el código; y cada vez que se asciende en
  9497.   un brazo a la derecha se coloca un 1 a la izquierda del código.
  9498.  
  9499.   Observa que al construir el árbol y al obtener los códigos, es necesario
  9500.   únicamente el de guardar los enlaces entre cada nodo con su padre y una
  9501.   indicación con respecto a si el nodo es un hijo izquierdo o derecho; los
  9502.   enlaces del padre con cualquiera de sus dos hijos no es necesario. Cada
  9503.   nodo contiene tres campos: padre, tipo de hijo, y frecuencia. El campo
  9504.   padre es un puntero al nodo padre. Si el nodo es la raíz, su campo de
  9505.   padre será nulo. El valor del campo tipo de hijo es una de las constantes
  9506.   hijo izquierdo o hijo derecho dependiendo de si el nodo es un hijo iz-
  9507.   quierdo o derecho. El campo frecuencia es la frecuencia de ocurrencia del
  9508.   símbolo representado por ese nodo.
  9509.  
  9510.  
  9511.   CODIFICACION:
  9512.  
  9513.   El algoritmo seguido en el programa ha sido el algoritmo de Huffman. Las
  9514.   estructuras utilizadas son las siguientes:
  9515.       1)
  9516.        enum tipo_hijo { hijo_izq, hijo_der };
  9517.  
  9518.        struct nodo_arbol_asc
  9519.          {
  9520.            enum tipo_hijo tipo_hijo;
  9521.            struct nodo_arbol_asc *ppadre;
  9522.          };
  9523.  
  9524.        typedef struct nodo_arbol_asc *punt_arbol_asc;
  9525.  
  9526.        Este árbol es el que se utiliza en el proceso de compresión y se trata
  9527.        de un árbol ascendente.
  9528.  
  9529.       2)
  9530.        struct nodo_arbol_desc
  9531.          {
  9532.            uchar caracter;
  9533.            struct nodo_arbol_desc *phijoizq, *phijoder;
  9534.          };
  9535.  
  9536.        typedef struct nodo_arbol_desc *punt_arbol_desc;
  9537.  
  9538.        Este árbol es el que se utiliza en el proceso de descompresión y se
  9539.        trata de un árbol descendente, es decir, para acceder a cualquier
  9540.        nodo de él, sólo necesitamos un puntero a la cabeza del árbol.
  9541.  
  9542.       3)
  9543.        struct nodo_lista_frecuencias
  9544.        {
  9545.          uchar caracter;
  9546.          ulong frecuencia;
  9547.          union
  9548.            {
  9549.              punt_arbol_asc puntarbolasc;
  9550.              punt_arbol_desc puntarboldesc;
  9551.            } puntarbol;
  9552.          struct nodo_lista_frecuencias *psiguiente;
  9553.        };
  9554.  
  9555.        typedef struct nodo_lista_frecuencias *punt_lista_frecuencias;
  9556.  
  9557.        Se trata de una lista ordenada por frecuencias necesaria en el proceso
  9558.        de compresión para ir insertando en el árbol primero los caracteres que
  9559.        aparecen menos veces quedando así los caracteres que aparecen con más
  9560.        frecuencia en un nivel superior a los que aparecen con menos frecuencia,
  9561.        de este modo, a los que aparecen menos veces les corresponde un número
  9562.        menor de bits que a los que parecen más.
  9563.  
  9564.       4)
  9565.        typedef struct tabla
  9566.          {
  9567.            ulong frecuencia;
  9568.            punt_arbol_asc parbolasc;
  9569.          } tabla_de_caracteres [256];
  9570.  
  9571.        Tabla en la que cada entrada corresponde a uno de los 256 caracteres
  9572.        que componen el código ASCII.
  9573.  
  9574.       5)
  9575.        struct nodo_pila
  9576.          {
  9577.            uchar bit;
  9578.            struct nodo_pila *psig;
  9579.          };
  9580.  
  9581.        typedef struct nodo_pila *punt_pila;
  9582.  
  9583.        Pila necesaria en varios procesos de compresión y descompresión.
  9584.  
  9585.    El proceso seguido en este programa para implantar este algoritmo es el
  9586.    siguiente:
  9587.  
  9588.    - Compresión:
  9589.  
  9590.      Al leer los caracteres actualizo en tabchar la frecuencia de cada uno de
  9591.      ellos. Una vez leídos todos los caracteres, creo la lista de frecuencias
  9592.      ordenada de menor a mayor frecuencia. Mientras esta lista no esté vacía,
  9593.      cojo el primer elemento de la lista, si el puntero al árbol de este nodo
  9594.      es NULO es que se trata de un nuevo elemento del árbol por lo que inser-
  9595.      to el nuevo nodo en el árbol y guardo en la entrada  correspondiente  de
  9596.      tabchar un puntero al nodo del árbol,  si por el contrario el puntero al
  9597.      árbol de ese nodo no es NULO, es que se trata de un nodo existente ya en
  9598.      el árbol;  a  continuación leo el siguiente nodo de la lista de frecuen-
  9599.      cias y hago lo mismo que he hecho con el primer  nodo.  Ahora  mismo  ya
  9600.      tengo dos nodos (dos punteros al árbol)  que he de unir en el árbol a un
  9601.      sólo nodo,  este  nuevo  nodo  creado en el árbol lo inserto en el lugar
  9602.      correspondiente en la lista de frecuencias.  De  nuevo vuelvo a leer dos
  9603.      nodos de la lista de frecuencias y así  sucesivamente.  Repitiendo  esta
  9604.      operación  hasta  que  quede  un sólo nodo en la lista de frecuencias se
  9605.      crea el árbol. Una vez creado el árbol, el resto es muy simple.
  9606.  
  9607.    - Descompresión:
  9608.  
  9609.      El árbol se crea casi igual que en la compresión, la única diferencia es
  9610.      que las frecuencias de tabchar se cargan de la cabecera del  fichero,  y
  9611.      que a la hora de unir dos nodos del árbol,  en la compresión se hace que
  9612.      los nodos hijos apunten al nodo padre y en la descompresión se hace  que
  9613.      el nodo padre apunte a los nodos hijos. Por lo demás el proceso de crea-
  9614.      ción del árbol es igual que en la compresión como se puede  ver  en  las
  9615.      implementaciones en C.  Una  vez  creado  el árbol, la decodificación es
  9616.      bastante fácil.
  9617.  
  9618.  
  9619.   ESTADISTICAS
  9620.  
  9621.   Para comprobar el ahorro de espacio que produce este programa se han obte-
  9622.   nido los siguientes datos al comprimir los siguientes programas:
  9623.  
  9624.  bytes del fichero número de carácteres diferentes bytes del fichero comprimido
  9625.       origen              del fichero origen           (includo la cabecera)
  9626.  ----------------- ------------------------------- ----------------------------
  9627.          104                      41                          192 (185%)
  9628.          206                       3                           41  (20%)
  9629.        1.196                      84                          862  (72%)
  9630.        4.375                      83                        2.835  (65%)
  9631.       14.733                     253                       13.279  (90%)
  9632.       16.004                      19                        6.333  (40%)
  9633.       16.882                      83                        9.976  (59%)
  9634.       17.790                      89                       11.243  (63%)
  9635.       30.392                     256                       26.866  (88%)
  9636.       31.557                      84                       19.272  (61%)
  9637.       40.195                      93                       26.843  (67%)
  9638.      290.249                     256                      256.961  (88%)
  9639.       Media                     Media                         Media
  9640.       85.348                     133                       72.524  (85%)
  9641.  
  9642.  
  9643.   OBSERVACIONES FINALES:
  9644.  
  9645.     1) Habréis observado en el programa que los ficheros son binarios y no de
  9646.        texto. Esto ha de ser así forzosamente ya que los ficheros a manejar
  9647.        pueden tener caracteres fin de fichero (código ASCII 26) en su contenido.
  9648.  
  9649.     2) Para que se pueda ejecutar el programa desde el tutor se ha modificado
  9650.        un poco la función main para que el programa, en vez de aceptar los
  9651.        argumentos desde la línea de órdenes del sistema operativo, los lea
  9652.        con funciones de entrada estándar. Si se va a conviertir este ejemplo
  9653.        en un programa ejecutable, recomendaría utilizar la función main() que
  9654.        está entre comentarios y no la que se encuentra activa en este momento,
  9655.        en dicho caso la función mensaje_terminar() no sirve para nada y reco-
  9656.        miendo quitarla. Al compilar tened en cuenta que hay comentarios anidados
  9657.        en la función main() y puede haber compiladores que no permitan tal cosa.
  9658.  
  9659. */
  9660.  
  9661. /* Ficheros de cabecera: */
  9662.  
  9663. #include <string.h> /* strcmp() */
  9664. #include <ctype.h>  /* tolower() */
  9665. #include <stdlib.h> /* exit(), malloc(), free() */
  9666. #include <stdarg.h> /* va_list, va_start(), va_end() */
  9667. #include <stdio.h>  /* FILE, fopen(), fclose(), NULL, EOF, feof(), printf(),
  9668.             putc(), getc(), fread(), fwrite(), puts(), vfprintf(), rewind() */
  9669.  
  9670. /* Constantes y macros: */
  9671.  
  9672. #define BOOLEAN short
  9673. #define TRUE    1
  9674. #define FALSE   0
  9675.  
  9676. #define CREADOR "Antonio Lebrón Bocanegra"
  9677. #define MARCA_DE_RECONOCIMIENTO "ALB"
  9678.  
  9679. #define uchar unsigned char
  9680. #define ulong unsigned long
  9681.  
  9682. #define t(ind) (*(tabchar+(ind)))
  9683.  
  9684. /* Estructuras y tipos: */
  9685.  
  9686. enum tipo_hijo { hijo_izq, hijo_der };
  9687.  
  9688. struct nodo_arbol_asc
  9689.   {
  9690.     enum tipo_hijo tipo_hijo;
  9691.     struct nodo_arbol_asc *ppadre;
  9692.   };
  9693.  
  9694. struct nodo_arbol_desc
  9695.   {
  9696.     uchar caracter;
  9697.     struct nodo_arbol_desc *phijoizq, *phijoder;
  9698.   };
  9699.  
  9700. typedef struct nodo_arbol_asc *punt_arbol_asc;
  9701. typedef struct nodo_arbol_desc *punt_arbol_desc;
  9702.  
  9703. struct nodo_lista_frecuencias
  9704. {
  9705.   uchar caracter;
  9706.   ulong frecuencia;
  9707.   union
  9708.     {
  9709.       punt_arbol_asc puntarbolasc;
  9710.       punt_arbol_desc puntarboldesc;
  9711.     } puntarbol;
  9712.   struct nodo_lista_frecuencias *psiguiente;
  9713. };
  9714.  
  9715. typedef struct nodo_lista_frecuencias *punt_lista_frecuencias;
  9716.  
  9717. typedef struct tabla
  9718.   {
  9719.     ulong frecuencia;
  9720.     punt_arbol_asc parbolasc;
  9721.   } tabla_de_caracteres [256];
  9722.  
  9723. struct nodo_pila
  9724.   {
  9725.     uchar bit;
  9726.     struct nodo_pila *psig;
  9727.   };
  9728.  
  9729. typedef struct nodo_pila *punt_pila;
  9730.  
  9731. enum tipo_fichero { lectura, escritura };
  9732.  
  9733. /* Variables globales: */
  9734.  
  9735. BOOLEAN comprimir;
  9736. tabla_de_caracteres tabchar;
  9737. int nchardiferentes = 0;
  9738. FILE *pfichero_origen, *pfichero_destino;
  9739.  
  9740. /* Prototipos de funciones ordenadas alfabéticamente por su nombre: */
  9741.  
  9742. FILE *abrir_fichero (char *nombre, enum tipo_fichero tipo_fich);
  9743. void apilar_bits (punt_pila *pila, int indice);
  9744. void apilar_nodo (punt_pila *pila, uchar bit);
  9745. void ayuda (void);
  9746. void leer_cabecera_cargando_tabchar (void);
  9747. void cerrar_fichero (FILE *pfich, char *nombre_fichero);
  9748. void crear_arbol_asc (punt_lista_frecuencias *plista);
  9749. punt_arbol_desc crear_arbol_desc (punt_lista_frecuencias *plista);
  9750. punt_arbol_asc crear_nodo_arbol_asc (void);
  9751. punt_arbol_desc crear_nodo_arbol_desc (uchar ch);
  9752. void desapilar_bits (punt_pila *pila);
  9753. void desapilar_byte (punt_pila *pila, uchar *num_bits_significativos_de_byte, uchar *byte);
  9754. void empezar_programa (void);
  9755. void error_en_programa (char *lista_de_argumentos, ...);
  9756. void escribir_cabecera (void);
  9757. void escribir_caracteres_comprimidos (void);
  9758. void escribir_caracteres_descomprimidos (punt_arbol_desc parbol);
  9759. punt_lista_frecuencias inicializar_lista_frec (void);
  9760. void inicializar_tabla_para_compresion (void);
  9761. void inicializar_tabla_para_descompresion (void);
  9762. void insertar_en_orden_en_lista_frec(punt_lista_frecuencias *plist, punt_lista_frecuencias pl);
  9763. void insertar_nodo_arbol_asc_en_lista_frecuencias (punt_lista_frecuencias *plist, punt_arbol_asc parbol, ulong frecuencia);
  9764. void insertar_nodo_arbol_desc_en_lista_frecuencias (punt_lista_frecuencias *plist, punt_arbol_desc parbol, ulong frecuencia);
  9765. void leer_fichero_origen_cargando_tabchar (void);
  9766. void mensaje_terminar (void);
  9767. void proceso_compresion (void);
  9768. void proceso_descompresion (void);
  9769. void proceso_descomprimir (uchar *nomb_fich_origen, uchar *nomb_fich_destino);
  9770. void proceso (char *nombre_fich_origen, char *nombre_fich_destino);
  9771. punt_arbol_asc unir_nodos_arbol_asc (punt_arbol_asc parb[2]);
  9772. punt_arbol_desc unir_nodos_arbol_desc (punt_arbol_desc parb[2]);
  9773.  
  9774. /* Funciones: */
  9775.  
  9776. /*****************************************************************************/
  9777.  
  9778.                 /* FUNCION PRINCIPAL Y FUNCIONES VARIAS */
  9779.  
  9780. /*****************************************************************************/
  9781.  
  9782. /*
  9783. /*
  9784.   Función principal. Analiza argumentos y si son correctos activa proceso
  9785.   principal.
  9786. */
  9787.  
  9788. void main (int argc, uchar *argv[])
  9789. {
  9790.   if (argc != 4)
  9791.     ayuda ();
  9792.   else
  9793.     {
  9794.       if (tolower (*argv[1]) != 'c' && tolower (*argv[1]) != 'd')
  9795.         error_en_programa ("Error en segundo argumento.");
  9796.       comprimir = tolower (*argv[1]) == 'c';
  9797.       proceso (argv[2], argv[3]);
  9798.     }
  9799.   exit (0);
  9800. } /* main */
  9801. */
  9802.  
  9803. void main (void)
  9804. {
  9805.   #include <stdio.h> /* gets(), printf(), puts(), putch() */
  9806.   #include <conio.h> /* getch() */
  9807.  
  9808.   char ch, nombre_fichero_origen[256], nombre_fichero_destino[256];
  9809.  
  9810.   puts ("\nPROGRAMA COMPRESOR/DESCOMPRESOR DE FICHEROS.");
  9811.   printf ("\n¿Comprimir o Descomprimir (C/D)? " );
  9812.   do
  9813.     {
  9814.       ch = getch ();
  9815.     } while (tolower (ch) != 'c' && tolower (ch) != 'd');
  9816.   comprimir = tolower (putch (ch)) == 'c';
  9817.   printf ("\nIntroduce fichero origen: ");
  9818.   do
  9819.     {
  9820.       gets (nombre_fichero_origen);
  9821.     } while (!*nombre_fichero_origen);
  9822.   printf ("Introduce fichero destino: ");
  9823.   do
  9824.     {
  9825.       gets (nombre_fichero_destino);
  9826.     } while (!*nombre_fichero_destino);
  9827.   puts ("");
  9828.   proceso (nombre_fichero_origen, nombre_fichero_destino);
  9829.   mensaje_terminar ();
  9830.   exit (0);
  9831. } /* main */
  9832.  
  9833. void mensaje_terminar (void)
  9834. {
  9835.   puts ("\nPulsa cualquier tecla para finalizar. ");
  9836.   getch ();
  9837. }
  9838.  
  9839. /*****************************************************************************/
  9840.  
  9841. /*
  9842.   Función llamada cuando los argumentos pasados al programa son incorrectos.
  9843.   Visualiza una breve información acerca del programa.
  9844. */
  9845.  
  9846. void ayuda (void)
  9847. {
  9848.   printf ("\nPrograma compresor/descompresor de ficheros por %s\n\n", CREADOR);
  9849.   puts ("Sintaxis:");
  9850.   puts ("  nombre_programa  [c|d]  fichero_origen  fichero_destino");
  9851. } /* ayuda */
  9852.  
  9853. /*****************************************************************************/
  9854.  
  9855. /*
  9856.   Función llamada siempre que ocurra algún tipo de error en el programa.
  9857.   Visualiza mensaje de error y finaliza programa.
  9858. */
  9859.  
  9860. void error_en_programa (char *lista_de_argumentos, ...)
  9861. {
  9862.   va_list puntero_a_los_argumentos;
  9863.  
  9864.   va_start (puntero_a_los_argumentos, lista_de_argumentos);
  9865.   vfprintf (stderr, lista_de_argumentos, puntero_a_los_argumentos);
  9866.   va_end (puntero_a_los_argumentos);
  9867.  
  9868.   mensaje_terminar ();
  9869.   exit (1);
  9870. } /* error_en_programa */
  9871.  
  9872. /*****************************************************************************/
  9873.  
  9874. /*
  9875.   Abre fichero dado en modo de apertura especificado comprobando que la
  9876.   apertura se hace correctamente.
  9877. */
  9878.  
  9879. FILE *abrir_fichero (char *nombre, enum tipo_fichero tipo_fich)
  9880. {
  9881.   FILE *pfi;
  9882.  
  9883.   if ((pfi = fopen (nombre, tipo_fich == escritura ? "wb" : "rb")) == NULL)
  9884.     error_en_programa ("Error al intentar abrir el fichero '%s'.", nombre);
  9885.   return (pfi);
  9886. } /* abrir_fichero */
  9887.  
  9888. /*****************************************************************************/
  9889.  
  9890. /*
  9891.   Cierra fichero dado comprobando que la operación se realiza correctamente.
  9892. */
  9893.  
  9894. void cerrar_fichero (FILE *pfich, char *nombre_fichero)
  9895. {
  9896.   if (fclose (pfich) == EOF)
  9897.     error_en_programa ("Error al cerrar el fichero '%s'.", nombre_fichero);
  9898. } /* cerrar_fichero */
  9899.  
  9900. /*****************************************************************************/
  9901.  
  9902.              /* FUNCIONES PRINCIPALES RELACIONADAS CON LOS
  9903.                 PROCESOS DE COMPRIMIR Y DESCOMPRIMIR */
  9904.  
  9905. /*****************************************************************************/
  9906.  
  9907. /*
  9908.   Abre ficheros, llama al proceso de compresión o descompresión según
  9909.   argumentos del programa, y cierra ficheros cuando el proceso de
  9910.   compresión o descompresión ha finalizado.
  9911. */
  9912.  
  9913. void proceso (char *nombre_fich_origen, char *nombre_fich_destino)
  9914. {
  9915.   pfichero_origen = abrir_fichero (nombre_fich_origen, lectura);
  9916.   pfichero_destino = abrir_fichero (nombre_fich_destino, escritura);
  9917.  
  9918.   comprimir ? proceso_compresion () : proceso_descompresion ();
  9919.  
  9920.   cerrar_fichero (pfichero_origen, nombre_fich_origen);
  9921.   cerrar_fichero (pfichero_destino, nombre_fich_destino);
  9922. } /* proceso */
  9923.  
  9924. /*****************************************************************************/
  9925.  
  9926. /*
  9927.   Esta función es la que ejecuta el proceso de compresión mediante varias
  9928.   llamadas a otras funciones.
  9929. */
  9930.  
  9931. void proceso_compresion (void)
  9932. {
  9933.   punt_lista_frecuencias plistafrec;
  9934.  
  9935.   puts ("Comprimiendo...");
  9936.   inicializar_tabla_para_compresion ();
  9937.   leer_fichero_origen_cargando_tabchar ();
  9938.   plistafrec = inicializar_lista_frec ();
  9939.   crear_arbol_asc (&plistafrec);
  9940.   escribir_caracteres_comprimidos ();
  9941. }
  9942.  
  9943. /*****************************************************************************/
  9944.  
  9945. /*
  9946.   Esta función es la que ejecuta el proceso de descompresión mediante varias
  9947.   llamadas a otras funciones. El proceso seguido es similar al proceso de
  9948.   compresión.
  9949. */
  9950.  
  9951. void proceso_descompresion (void)
  9952. {
  9953.   punt_lista_frecuencias plistafrec;
  9954.   punt_arbol_desc p_cabeza_arbol;
  9955.  
  9956.   puts ("Descomprimiendo...");
  9957.   inicializar_tabla_para_descompresion ();
  9958.   leer_cabecera_cargando_tabchar ();
  9959.   plistafrec = inicializar_lista_frec ();
  9960.   p_cabeza_arbol = crear_arbol_desc (&plistafrec);
  9961.   escribir_caracteres_descomprimidos (p_cabeza_arbol);
  9962. }
  9963.  
  9964. /*****************************************************************************/
  9965.  
  9966. /*
  9967.   Inicializa a 0 todos los campos de cada elemento de la tabla tabchar.
  9968. */
  9969.  
  9970. void inicializar_tabla_para_compresion (void)
  9971. {
  9972.   register int i;
  9973.  
  9974.   for (i = 0; i < 256; i++)
  9975.     {
  9976.       t(i).frecuencia = 0;
  9977.       t(i).parbolasc = NULL;
  9978.     }
  9979. } /* inicializar_tabla_para_compresion */
  9980.  
  9981. /*****************************************************************************/
  9982.  
  9983. /*
  9984.   Inicializa a 0 el campo de frecuencia de todos los elementos de tabchar.
  9985. */
  9986.  
  9987. void inicializar_tabla_para_descompresion (void)
  9988. {
  9989.   register int i;
  9990.  
  9991.   for (i = 0; i < 256; i++)
  9992.     t(i).frecuencia = 0;
  9993. } /* inicializar_tabla_para_descompresion */
  9994.  
  9995. /*****************************************************************************/
  9996.  
  9997. /*
  9998.   Esta función es llamada por proceso_compresion() para rellenar el vector
  9999.   tabchar leyendo el fichero fuente completo.
  10000. */
  10001.  
  10002. void leer_fichero_origen_cargando_tabchar (void)
  10003. {
  10004.   register int ch;
  10005.  
  10006.   for (ch = getc (pfichero_origen); ! feof (pfichero_origen);
  10007.        ch = getc (pfichero_origen))
  10008.     if (t(ch).frecuencia++ == 0)
  10009.       ++nchardiferentes;
  10010. } /* leer_caracteres */
  10011.  
  10012. /*****************************************************************************/
  10013.  
  10014. /*
  10015.   Escribe caracteres comprimidos en fichero destino.
  10016.  
  10017.   Algoritmo:
  10018.     ┌ principio
  10019.     │  ** byte1 es el byte que se va a escribir y byte2 representa el número
  10020.     │  ** de bits significativos de byte1
  10021.     │  ┌ mientras no fin de datos hacer
  10022.     │  │  apilar bits en ppila correspondientes al caracter leído
  10023.     │  │  ┌ mientras ppila no sea NULO hacer
  10024.     │  │  │  desapilar byte de ppila actualizando byte1 y byte2
  10025.     │  │  │  ┌ si byte = 8 entonces
  10026.     │  │  │  │  ** se ha completado byte1
  10027.     │  │  │  │  byte2 = 0
  10028.     │  │  │  │  escribir byte1 en fichero de destino
  10029.     │  │  │  └ finsi
  10030.     │  │  └ finmientras
  10031.     │  └finmientras
  10032.     │  ┌ si byte2 es distinto de cero entonces
  10033.     │  │  ┌ para ch = 1 hasta 8 - byte2 con incremento 1 hacer
  10034.     │  │  │  desplazar un bit a la izquierda byte1
  10035.     │  │  └ finpara
  10036.     │  │  escribir byte1 en fichero de destino
  10037.     │  └ finsi
  10038.     │  escribir en fichero destino 8 si byte2 = 0 o byte2 si byte2 <> 0
  10039.     └ fin
  10040. */
  10041.  
  10042. void escribir_caracteres_comprimidos (void)
  10043. {
  10044.   punt_pila ppila = NULL;
  10045.   struct { uchar byte1, byte2; } byte;
  10046.   int ch;   /* byte1 es el byte propiamente dicho y byte2 es el número de bits
  10047.                significativos de byte1 */
  10048.  
  10049.   rewind (pfichero_origen);
  10050.   byte.byte1 = byte.byte2 = 0;
  10051.   escribir_cabecera ();
  10052.   for (ch = getc (pfichero_origen); ! feof (pfichero_origen);
  10053.        ch = getc (pfichero_origen))
  10054.     {
  10055.       apilar_bits (&ppila, ch);
  10056.       while (ppila != NULL)
  10057.         {
  10058.           desapilar_byte (&ppila, &byte.byte2, &byte.byte1);
  10059.           if (byte.byte2 == 8)
  10060.             {
  10061.               byte.byte2 = 0;
  10062.               putc (byte.byte1, pfichero_destino);
  10063.             }
  10064.         }
  10065.     }
  10066.   if (byte.byte2)
  10067.     {
  10068.       byte.byte1 <<= 8 - byte.byte2;
  10069.       putc (byte.byte1, pfichero_destino);
  10070.     }
  10071.   putc (byte.byte2 ? byte.byte2 : 8, pfichero_destino);
  10072. } /* escribir_caracteres_comprimidos */
  10073.  
  10074. /*****************************************************************************/
  10075.  
  10076. /*
  10077.   Escribe la cabecera en el fichero destino, la cual está compuesta de una
  10078.   marca de tres caracteres para reconocer que el fichero de origen está
  10079.   comprimido con este compresor, un carácter que representa cuántos carac-
  10080.   teres distintos hay, es decir, cuántos entradas vienen a continuación y
  10081.   finalmente tantas entradas como caracteres distintos hay constando cada
  10082.   entrada del carácter y su frecuencia.
  10083.  
  10084.   Algoritmo:
  10085.     ┌ principio
  10086.     │  escribir en fichero la marca de reconocimiento
  10087.     │  escribir en fichero el número de carácteres distintos que hay menos 1
  10088.     │  ** se le resta 1 para que quepa en un char ya que con un char sólo se
  10089.     │  ** pueden representar números desde el 0 hasta el 255 y puede haber
  10090.     │  ** 256 carácteres diferentes.
  10091.     │  ┌ para i = 0 hasta 255 con incremento 1 hacer
  10092.     │  │  ┌ si tabchar[i].frecuencia es distinto de cero entonces
  10093.     │  │  │  escribir en fichero caracter y frecuencia
  10094.     │  │  └ finsi
  10095.     │  └ finpara
  10096.     └ fin
  10097. */
  10098.  
  10099. void escribir_cabecera (void)
  10100. {
  10101.   struct caracteres { uchar ch; ulong frec; } caracter;
  10102.   uchar nchardif = nchardiferentes - 1; /* para que quepa en un uchar (0-255) */
  10103.   register int i;
  10104.   char *marca_de_reconoc = MARCA_DE_RECONOCIMIENTO;
  10105.  
  10106.   fwrite (marca_de_reconoc, strlen (MARCA_DE_RECONOCIMIENTO), 1,
  10107.           pfichero_destino);
  10108.   putc (nchardif, pfichero_destino);
  10109.   for (i = 0; i < 256; i++)
  10110.     if (t(i).frecuencia)
  10111.       {
  10112.         caracter.ch = i;
  10113.         caracter.frec = t(i).frecuencia;
  10114.         fwrite (&caracter, sizeof (struct caracteres), 1, pfichero_destino);
  10115.       }
  10116. } /* escribir_cabecera */
  10117.  
  10118. /*****************************************************************************/
  10119.  
  10120. /*
  10121.   Esta función es llamada por proceso_descompresion() para rellenar el vector
  10122.   tabchar leyendo la cabecera del fichero destino. Al mismo tiempo comprueba
  10123.   que el fichero a descomprimir es un fichero comprimido previamente con este
  10124.   compresor.
  10125. */
  10126.  
  10127. void leer_cabecera_cargando_tabchar (void)
  10128. {
  10129.   struct caracteres { uchar ch; ulong frec; } caracter;
  10130.   int nchardif;
  10131.   register int i;
  10132.   char *marca_de_reconoc = MARCA_DE_RECONOCIMIENTO;
  10133.  
  10134.   fread (marca_de_reconoc, strlen (MARCA_DE_RECONOCIMIENTO), 1,
  10135.          pfichero_origen);
  10136.   if (strcmp (marca_de_reconoc, MARCA_DE_RECONOCIMIENTO))
  10137.     error_en_programa ("No es un programa comprimido con este compresor.");
  10138.   nchardif = getc (pfichero_origen) + 1;
  10139.   for (i = 1 ; i <= nchardif ; i++)
  10140.     {
  10141.       fread (&caracter, sizeof (struct caracteres), 1, pfichero_origen);
  10142.       t(caracter.ch).frecuencia = caracter.frec;
  10143.     }
  10144. } /* leer_cabecera_cargando_tabchar */
  10145.  
  10146. /*****************************************************************************/
  10147.  
  10148. /*
  10149.   Escribe caracteres descomprimidos en fichero destino.
  10150.  
  10151.   Algoritmo:
  10152.     ┌ principio
  10153.     │  leer ch, ch1 y ch2 del fichero origen
  10154.     │  ** lee tres caracteres para cuando ch2 = EOF, entonces el valor de
  10155.     │  ** ch1 representará los bits significativos de ch
  10156.     │  parbolaux = parbol
  10157.     │  salir = FALSO
  10158.     │  nbits = 0
  10159.     │  ┌ mientras salir = FALSO entonces
  10160.     │  │  ┌ mientras (nbits < 8 si ch2 <> EOF ó nbits < ch1 si ch2 = EOF)
  10161.     │  │  │    y parbolaux->phijoizq <> NULO y parbolaux->phijoder <> NULO))
  10162.     │  │  │  ┌ si primer bit de ch es 1 entonces
  10163.     │  │  │  │  parbolaux = parbolaux->phijoder
  10164.     │  │  │  │ sino
  10165.     │  │  │  │  parbolaux = parbolaux->phijoizq
  10166.     │  │  │  └ finsi
  10167.     │  │  │  nbits = bbits + 1
  10168.     │  │  │  ch = ch desplazado un bit a la izquierda
  10169.     │  │  └ finmientras
  10170.     │  │  ┌ si parbolaux->phijoizq = NULO y parbolaux->phijoder = NULO
  10171.     │  │  │  escribir parbolaux->caracter en fichero de destino
  10172.     │  │  │  parbolaux = parbol
  10173.     │  │  └ finsi
  10174.     │  │  ┌ si nbits = 8 en caso ch2 <> EOF o nbits = ch1 en caso ch2 = EOF
  10175.     │  │  │  nbits = 0
  10176.     │  │  │  ┌ si ch2 = EOF entonces
  10177.     │  │  │  │  salir = VERDAD
  10178.     │  │  │  │ sino
  10179.     │  │  │  │  ch = ch1
  10180.     │  │  │  │  ch1 = ch2
  10181.     │  │  │  │  ch2 = obtener carácter de fichero origen
  10182.     │  │  │  └ finsi
  10183.     │  │  └ finsi
  10184.     │  └ finmientras
  10185.     └ fin
  10186. */
  10187.  
  10188. void escribir_caracteres_descomprimidos (punt_arbol_desc parbol)
  10189. {
  10190.   int ch, ch1, ch2, nbits = 0;
  10191.   BOOLEAN salir = FALSE, ff;
  10192.   punt_arbol_desc parbolaux = parbol;
  10193.  
  10194.   ch = getc (pfichero_origen);
  10195.   if (feof (pfichero_origen))
  10196.     error_en_programa ("El fichero comprimido no es correcto.");
  10197.   ch1 = getc (pfichero_origen);
  10198.   if (feof (pfichero_origen))
  10199.     error_en_programa ("El fichero comprimido no es correcto.");
  10200.   ch2 = getc (pfichero_origen);
  10201.   ff = feof (pfichero_origen);
  10202.   while (! salir)
  10203.     {
  10204.       while (nbits < (ff ? ch1 : 8) && parbolaux->phijoizq != NULL)
  10205.         {
  10206.           parbolaux = ch & 0x80 ? parbolaux->phijoder : parbolaux->phijoizq;
  10207.           ++nbits;
  10208.           ch <<= 1;
  10209.         }
  10210.       if (parbolaux->phijoizq == NULL)
  10211.         {
  10212.           putc (parbolaux->caracter, pfichero_destino);
  10213.           parbolaux = parbol;
  10214.         }
  10215.       if (nbits == (ff ? ch1 : 8))
  10216.         {
  10217.           nbits = 0;
  10218.           if (ff)
  10219.             salir = TRUE;
  10220.           else
  10221.             {
  10222.               ch = ch1;
  10223.               ch1 = ch2;
  10224.               ch2 = getc (pfichero_origen);
  10225.               ff = feof (pfichero_origen);
  10226.             }
  10227.         }
  10228.     }
  10229. } /* escribir_caracteres_descomprimidos */
  10230.  
  10231. /*****************************************************************************/
  10232.  
  10233.               /* FUNCIONES RELACIONADAS CON EL ARBOL ASCENDENTE */
  10234.  
  10235. /*****************************************************************************/
  10236.  
  10237. void crear_arbol_asc (punt_lista_frecuencias *plista)
  10238. {
  10239.   register short i;
  10240.   punt_arbol_asc parb[2];
  10241.   punt_lista_frecuencias plistaaux;
  10242.   ulong suma_frecuencias;
  10243.  
  10244.   while (*plista != NULL && (*plista)->psiguiente != NULL)
  10245.     {
  10246.       suma_frecuencias = 0;
  10247.       for (i = 0 ; i <= 1; i++)
  10248.         {
  10249.           if ((*plista)->puntarbol.puntarbolasc == NULL)
  10250.             t((*plista)->caracter).parbolasc = parb[i] = crear_nodo_arbol_asc ();
  10251.           else
  10252.             parb[i] = (*plista)->puntarbol.puntarbolasc;
  10253.           suma_frecuencias += (*plista)->frecuencia;
  10254.           plistaaux = *plista;
  10255.           *plista = (*plista)->psiguiente;
  10256.           free (plistaaux);
  10257.         }
  10258.       insertar_nodo_arbol_asc_en_lista_frecuencias (plista,
  10259.                              unir_nodos_arbol_asc (parb), suma_frecuencias);
  10260.     }
  10261.   free (*plista);
  10262. } /* crear_abol_asc */
  10263.  
  10264. /*****************************************************************************/
  10265.  
  10266. punt_arbol_asc crear_nodo_arbol_asc (void)
  10267. {
  10268.   punt_arbol_asc parbolaux = (punt_arbol_asc) malloc
  10269.                                              (sizeof (struct nodo_arbol_asc));
  10270.   if ( parbolaux == NULL )
  10271.     error_en_programa ("No hay suficiente memoria.");
  10272.   return (parbolaux);
  10273. } /* crear_nodo_arbol_asc */
  10274.  
  10275. /*****************************************************************************/
  10276.  
  10277. punt_arbol_asc unir_nodos_arbol_asc (punt_arbol_asc parb[2])
  10278. {
  10279.   punt_arbol_asc parbolaux = (punt_arbol_asc) malloc
  10280.                                               (sizeof (struct nodo_arbol_asc));
  10281.   if (parbolaux == NULL)
  10282.     error_en_programa ("No hay suficiente memoria.");
  10283.   (*parb)->tipo_hijo = hijo_izq;
  10284.   (*(parb + 1))->tipo_hijo = hijo_der;
  10285.   parbolaux->ppadre = NULL;
  10286.   (*parb)->ppadre = (*(parb + 1))->ppadre = parbolaux;
  10287.   return (parbolaux);
  10288. } /* unir_nodos_arbol_asc */
  10289.  
  10290. /*****************************************************************************/
  10291.  
  10292. void insertar_nodo_arbol_asc_en_lista_frecuencias
  10293.        (punt_lista_frecuencias *plist, punt_arbol_asc parbol, ulong frecuencia)
  10294. {
  10295.   punt_lista_frecuencias plistaaux = (punt_lista_frecuencias) malloc
  10296.                                       (sizeof (struct nodo_lista_frecuencias));
  10297.  
  10298.   if (plistaaux == NULL)
  10299.     error_en_programa ("No hay suficiente memoria.");
  10300.   else
  10301.     {
  10302.       plistaaux->frecuencia = frecuencia;
  10303.       plistaaux->puntarbol.puntarbolasc = parbol;
  10304.       plistaaux->psiguiente = NULL;
  10305.       insertar_en_orden_en_lista_frec (plist, plistaaux);
  10306.     }
  10307. } /* insertar_nodo_arbol_asc_en_lista_frecuencias */
  10308.  
  10309. /*****************************************************************************/
  10310.  
  10311.               /* FUNCIONES RELACIONADAS CON EL ARBOL DESCENDENTE */
  10312.  
  10313. /*****************************************************************************/
  10314.  
  10315. punt_arbol_desc crear_arbol_desc (punt_lista_frecuencias *plista)
  10316. {
  10317.   register short i;
  10318.   punt_arbol_desc parb[2], p_arbol_nodo_raiz;
  10319.   punt_lista_frecuencias plistaaux;
  10320.   ulong suma_frecuencias;
  10321.  
  10322.   while (*plista != NULL && (*plista)->psiguiente != NULL)
  10323.     {
  10324.       suma_frecuencias = 0;
  10325.       for (i = 0 ; i <= 1; i++)
  10326.         {
  10327.           if ((*plista)->puntarbol.puntarboldesc == NULL)
  10328.             parb[i] = crear_nodo_arbol_desc ((*plista)->caracter);
  10329.           else
  10330.             parb[i] = (*plista)->puntarbol.puntarboldesc;
  10331.           suma_frecuencias += (*plista)->frecuencia;
  10332.           plistaaux = *plista;
  10333.           *plista = (*plista)->psiguiente;
  10334.           free (plistaaux);
  10335.         }
  10336.       insertar_nodo_arbol_desc_en_lista_frecuencias (plista,
  10337.                       unir_nodos_arbol_desc (parb), suma_frecuencias);
  10338.     }
  10339.   p_arbol_nodo_raiz = (*plista)->puntarbol.puntarboldesc;
  10340.   free (*plista);
  10341.   return (p_arbol_nodo_raiz);
  10342. } /* crear_abol_desc */
  10343.  
  10344. /*****************************************************************************/
  10345.  
  10346. punt_arbol_desc crear_nodo_arbol_desc (uchar ch)
  10347. {
  10348.   punt_arbol_desc parbolaux = (punt_arbol_desc) malloc
  10349.                                    (sizeof (struct nodo_arbol_desc));
  10350.   if (parbolaux == NULL)
  10351.     error_en_programa ("No hay suficiente memoria.");
  10352.   parbolaux->caracter = ch;
  10353.   parbolaux->phijoizq = parbolaux->phijoder = NULL;
  10354.   return (parbolaux);
  10355. } /* crear_nodo_arbol_desc */
  10356.  
  10357. /*****************************************************************************/
  10358.  
  10359. punt_arbol_desc unir_nodos_arbol_desc (punt_arbol_desc parb[2])
  10360. {
  10361.   punt_arbol_desc parbolaux = (punt_arbol_desc) malloc
  10362.                                (sizeof (struct nodo_arbol_desc));
  10363.  
  10364.   if (parbolaux == NULL)
  10365.     error_en_programa ("No hay suficiente memoria.");
  10366.   parbolaux->phijoizq = *parb;
  10367.   parbolaux->phijoder = *(parb + 1);
  10368.   return (parbolaux);
  10369. } /* unir_nodos_arbol_asc */
  10370.  
  10371. /*****************************************************************************/
  10372.  
  10373. void insertar_nodo_arbol_desc_en_lista_frecuencias
  10374.      (punt_lista_frecuencias *plist, punt_arbol_desc parbol, ulong frecuencia)
  10375. {
  10376.   punt_lista_frecuencias plistaaux = (punt_lista_frecuencias) malloc
  10377.                                       (sizeof (struct nodo_lista_frecuencias));
  10378.  
  10379.   if (plistaaux == NULL)
  10380.     error_en_programa ("No hay suficiente memoria.");
  10381.   else
  10382.     {
  10383.       plistaaux->frecuencia = frecuencia;
  10384.       plistaaux->puntarbol.puntarboldesc = parbol;
  10385.       plistaaux->psiguiente = NULL;
  10386.       insertar_en_orden_en_lista_frec (plist, plistaaux);
  10387.     }
  10388. } /* insertar_nodo_arbol_desc_en_lista_frecuencias */
  10389.  
  10390. /*****************************************************************************/
  10391.  
  10392.             /* FUNCIONES RELACIONADAS CON EL MANEJO DE LAS LISTAS */
  10393.  
  10394. /*****************************************************************************/
  10395.  
  10396. punt_lista_frecuencias inicializar_lista_frec (void)
  10397. {
  10398.   register int i;
  10399.   punt_lista_frecuencias plista = NULL, plistaaux;
  10400.  
  10401.   for (i = 0; i < 256; i++)
  10402.     if (t(i).frecuencia)
  10403.       {
  10404.         plistaaux = (punt_lista_frecuencias) malloc (sizeof (struct
  10405.                                                      nodo_lista_frecuencias));
  10406.         if (plistaaux == NULL)
  10407.           error_en_programa ("No hay suficiente memoria.");
  10408.         plistaaux->caracter = i;
  10409.         plistaaux->frecuencia = t(i).frecuencia;
  10410.         if (comprimir)
  10411.           plistaaux->puntarbol.puntarbolasc = NULL;
  10412.         else
  10413.           plistaaux->puntarbol.puntarboldesc = NULL;
  10414.         plistaaux->psiguiente = NULL;
  10415.         insertar_en_orden_en_lista_frec (&plista, plistaaux);
  10416.       }
  10417.   return (plista);
  10418. } /* incializar_lista_frec */
  10419.  
  10420. /*****************************************************************************/
  10421.  
  10422. void insertar_en_orden_en_lista_frec (punt_lista_frecuencias *plist,
  10423.                                       punt_lista_frecuencias pl)
  10424. {
  10425.   if (*plist == NULL)
  10426.     *plist = pl;
  10427.   else
  10428.     {
  10429.       punt_lista_frecuencias plaux;
  10430.       uchar caractertemp;
  10431.       ulong frecuenciatemp;
  10432.       punt_arbol_asc parbolasctemp;
  10433.       punt_arbol_desc parboldesctemp;
  10434.  
  10435.       plaux = *plist;
  10436.       while (plaux->frecuencia <= pl->frecuencia && plaux->psiguiente != NULL)
  10437.         plaux = plaux->psiguiente;
  10438.       if (plaux->psiguiente == NULL)
  10439.         plaux->psiguiente = pl;
  10440.       else
  10441.         { /* insertar pl delante de plaux en *plist */
  10442.           if (plaux == *plist)
  10443.             {
  10444.               pl->psiguiente = *plist;
  10445.               *plist = pl;
  10446.             }
  10447.           else
  10448.             {
  10449.               pl->psiguiente = plaux->psiguiente;
  10450.               plaux->psiguiente = pl;
  10451.               /* intercambiar información entre pl y plaux */
  10452.               caractertemp = pl->caracter;
  10453.               pl->caracter = plaux->caracter;
  10454.               plaux->caracter = caractertemp;
  10455.               frecuenciatemp = pl->frecuencia;
  10456.               pl->frecuencia = plaux->frecuencia;
  10457.               plaux->frecuencia = frecuenciatemp;
  10458.               if (comprimir)
  10459.                 {
  10460.                   parbolasctemp = pl->puntarbol.puntarbolasc;
  10461.                   pl->puntarbol.puntarbolasc = plaux->puntarbol.puntarbolasc;
  10462.                   plaux->puntarbol.puntarbolasc = parbolasctemp;
  10463.                 }
  10464.               else
  10465.                 {
  10466.                   parboldesctemp = pl->puntarbol.puntarboldesc;
  10467.                   pl->puntarbol.puntarboldesc = plaux->puntarbol.puntarboldesc;
  10468.                   plaux->puntarbol.puntarboldesc = parboldesctemp;
  10469.                 }
  10470.             }
  10471.         }
  10472.       }
  10473. } /* insertar_en_orden_en_lista_frec */
  10474.  
  10475. /*****************************************************************************/
  10476.  
  10477. void apilar_bits (punt_pila *pila, int indice)
  10478. {
  10479.   punt_arbol_asc parbol = t(indice).parbolasc;
  10480.  
  10481.   while (parbol->ppadre != NULL)
  10482.     {
  10483.       apilar_nodo (pila, parbol->tipo_hijo == hijo_izq ? '0' : '1');
  10484.       parbol = parbol->ppadre;
  10485.     }
  10486. } /* apilar_bits */
  10487.  
  10488. /*****************************************************************************/
  10489.  
  10490. void apilar_nodo (punt_pila *pila, uchar bit)
  10491. {
  10492.   punt_pila ppilaaux = (punt_pila) malloc (sizeof (struct nodo_pila));
  10493.  
  10494.   if (ppilaaux == NULL)
  10495.     error_en_programa ("No hay suficiente memoria.");
  10496.   else
  10497.     ppilaaux->bit = bit;
  10498.   if (*pila == NULL)
  10499.     {
  10500.       *pila = ppilaaux;
  10501.       (*pila)->psig = NULL;
  10502.     }
  10503.   else
  10504.     {
  10505.       ppilaaux->psig = *pila;
  10506.       *pila = ppilaaux;
  10507.     }
  10508. } /* apilar_nodo */
  10509.  
  10510. /*****************************************************************************/
  10511.  
  10512. void desapilar_bits (punt_pila *pila)
  10513. {
  10514.   punt_pila pilaux;
  10515.  
  10516.   while (*pila != NULL)
  10517.     {
  10518.       pilaux = *pila;
  10519.       *pila = (*pila)->psig;
  10520.       free (pilaux);
  10521.     }
  10522. } /* desapilar_bits */
  10523.  
  10524. /*****************************************************************************/
  10525.  
  10526. void desapilar_byte (punt_pila *pila, uchar *num_bits_significativos_de_byte,
  10527.                      uchar *byte)
  10528. {
  10529.   punt_pila pilaux;
  10530.  
  10531.   while (*pila != NULL && *num_bits_significativos_de_byte < 8)
  10532.     {
  10533.       pilaux = *pila;
  10534.       *pila = (*pila)->psig;
  10535.       *byte = (*byte << 1) | (pilaux->bit == '1' ? 1 : 0);
  10536.       ++*num_bits_significativos_de_byte;
  10537.       free (pilaux);
  10538.     }
  10539. } /* desapilar_byte */
  10540.  
  10541. /*****************************************************************************/
  10542. ende
  10543. begint
  10544. begine " EJEMPLO SOBRE ALGUNAS FUNCIONES DE PANTALLA DE TURBO C "
  10545. /*
  10546.   Programa para probar algunas funciones de la librería <conio.h>.
  10547.   El objetivo de este ejemplo no es ser útil sino ser instructivo
  10548.   e ilustrativo respecto a las funciones de consola.
  10549. */
  10550.  
  10551. #include <conio.h>
  10552.  
  10553. void hacer_ventana (int x1, int y1, int x2, int y2, char *forma_caja,
  10554.                     int color_texto, int color_fondo);
  10555. void mostrar_limites_de_ventana (void);
  10556. void mostrar_funciones_de_borrado (void);
  10557. void mostrar_tipos_de_cursor (void);
  10558. void mostrar_funciones_de_texto (void);
  10559.  
  10560. void main (void)
  10561. {
  10562.   textmode (C80);
  10563.   hacer_ventana (5, 3, 75, 22, "╔═╗║╝═╚║", BLUE, CYAN);
  10564.   mostrar_limites_de_ventana ();
  10565.   mostrar_funciones_de_borrado ();
  10566.   mostrar_tipos_de_cursor ();
  10567.   mostrar_funciones_de_texto ();
  10568.   window (1, 1, 80, 25);
  10569.   textattr (0x07);
  10570.   clrscr ();
  10571.   cputs ("Pulsa una tecla para finalizar.");
  10572.   getch ();
  10573. }
  10574.  
  10575. /*
  10576.   Dibuja un recuadro desde (x1,y1) hasta (x2,y2) estando la forma del
  10577.   recuadro en la cadena forma_caja: el primer carácter de esta cadena
  10578.   correspondonde al carácter de la esquina superior izquierda, el
  10579.   segundo el de la línea superior horizontal y así consecutivamente,
  10580.   siguiente las manecillas del reloj, hasta llegar al último que es el
  10581.   carácter que representa la línea vertical izquierda.
  10582.  
  10583.   La ventana de texto (window) va en realidad desde (x1+1,y1+1) hasta
  10584.   (x2-1,y2-1).
  10585.  
  10586.   Fijaos que antes de utilizar el primer gotoxy() hemos hecho un window();
  10587.   esta llamada a window() es necesaria porque las coordenadas de gotoxy()
  10588.   están referidas a la ventana de texto actual. Del mismo modo, antes de
  10589.   hacer el primer putch() tenemos que llamar a las funciones textcolor()
  10590.   y textbackground() para que a partir de entonces los caracteres se escri-
  10591.   ban con el atributo de pantalla deseado.
  10592.  
  10593.   El clrscr() se hace al final de esta función para que el fondo de la
  10594.   ventana quede pintado en pantalla con el color color_fondo.
  10595. */
  10596.  
  10597. void hacer_ventana (int x1, int y1, int x2, int y2, char *forma_caja,
  10598.                     int color_texto, int color_fondo)
  10599. {
  10600.   register int i;
  10601.   const char esqsupizq = *(forma_caja),
  10602.              linhorsup = *(forma_caja + 1),
  10603.              esqsupder = *(forma_caja + 2),
  10604.              linverder = *(forma_caja + 3),
  10605.              esqinfder = *(forma_caja + 4),
  10606.              linhorinf = *(forma_caja + 5),
  10607.              esqinfizq = *(forma_caja + 6),
  10608.              linverizq = *(forma_caja + 7);
  10609.  
  10610.   window (1, 1, 80, 25);
  10611.  
  10612.   textcolor (color_texto);
  10613.   textbackground (color_fondo);
  10614.  
  10615.   for (i = x1 + 1; i < x2; i++)
  10616.     {
  10617.       gotoxy (i, y1);
  10618.       putch (linhorsup);
  10619.       gotoxy (i, y2);
  10620.       putch (linhorinf);
  10621.     }
  10622.  
  10623.   for (i = y1 + 1; i < y2; i++)
  10624.     {
  10625.       gotoxy (x1, i);
  10626.       putch (linverizq);
  10627.       gotoxy (x2, i);
  10628.       putch (linverder);
  10629.     }
  10630.  
  10631.   gotoxy (x1, y1);
  10632.   putch (esqsupizq);
  10633.  
  10634.   gotoxy (x2, y1);
  10635.   putch (esqsupder);
  10636.  
  10637.   gotoxy (x1, y2);
  10638.   putch (esqinfizq);
  10639.  
  10640.   gotoxy (x2, y2);
  10641.   putch (esqinfder);
  10642.  
  10643.   window (x1 + 1, y1 + 1, x2 - 1, y2 - 1);
  10644.  
  10645.   clrscr ();
  10646. }
  10647.  
  10648. /*
  10649.   Esta función muestra las funciones gettextinfo() y textattr().
  10650. */
  10651.  
  10652. void mostrar_limites_de_ventana (void)
  10653. {
  10654.   struct text_info ti;
  10655.  
  10656.   gettextinfo (&ti);
  10657.  
  10658.   window (1, 1, 80, 25);
  10659.   textattr (0x70);
  10660.  
  10661.   gotoxy (ti.winleft, ti.wintop);
  10662.   putch ('*');
  10663.  
  10664.   gotoxy (ti.winright, ti.wintop);
  10665.   putch ('*');
  10666.  
  10667.   gotoxy (ti.winleft, ti.winbottom);
  10668.   putch ('*');
  10669.  
  10670.   gotoxy (ti.winright, ti.winbottom);
  10671.   putch ('*');
  10672.  
  10673.   window (ti.winleft, ti.wintop, ti.winright, ti.winbottom);
  10674.   textattr (ti.attribute);
  10675.  
  10676.   gotoxy (2, 2);
  10677.   cputs ("  Los cuatro * marcan los bordes de la ventana de texto definida.");
  10678.  
  10679.   getch ();
  10680. }
  10681.  
  10682. /*
  10683.   Muestra las funciones: delline(), insline(), clreol() y clrscr().
  10684. */
  10685.  
  10686. void mostrar_funciones_de_borrado (void)
  10687. {
  10688.   cputs ("\n\n\rPulsa una tecla para borrar esta línea con delline().");
  10689.   getch ();
  10690.   delline ();
  10691.   getch ();
  10692.   insline ();
  10693.   cputs ("\n\n\rEsta línea ha sido insertada con la función insline()." );
  10694.   getch ();
  10695.   cputs ("\r");
  10696.   clreol ();
  10697.   getch ();
  10698.   cputs ("\rLínea borrada con clreol(): no desplaza líneas.");
  10699.   getch ();
  10700.   cputs ("\n\n\rPulsa una tecla para borrar pantalla.");
  10701.   getch ();
  10702.   clrscr ();
  10703.   getch ();
  10704. }
  10705.  
  10706. /*
  10707.   Muestra la función _setcursortype().
  10708. */
  10709.  
  10710. void mostrar_tipos_de_cursor (void)
  10711. {
  10712.   _setcursortype (_NORMALCURSOR);
  10713.   cputs ("\n\rCursor normal ");
  10714.   getch ();
  10715.   _setcursortype (_SOLIDCURSOR);
  10716.   cputs ("\n\rCursor sólido ");
  10717.   getch ();
  10718.   _setcursortype (_NOCURSOR);
  10719.   cputs ("\n\rCursor invisible ");
  10720.   getch ();
  10721.   _setcursortype (_NORMALCURSOR);
  10722.   clrscr ();
  10723. }
  10724.  
  10725. /*
  10726.   Muestra las funciones movetext(), gettext() y puttext().
  10727.   La variable buffer para estas tres funciones se suele declarar como
  10728.   en esta función. El 80 del tamaño se refiere al número de filas, el
  10729.   25 al número de columnas y el 2 se refiere a los dos bytes que hay
  10730.   que guardar en memoria por cada posición en la pantalla: un byte para
  10731.   el carácter y otro para el atributo de ese carácter en la pantalla.
  10732. */
  10733.  
  10734. void mostrar_funciones_de_texto (void)
  10735. {
  10736.   char buffer[80*25*2];
  10737.  
  10738.   cputs ("Pulsa una tecla para mover recuadro (3,3,10,10) a (20,5)");
  10739.   getch ();
  10740.   movetext (3, 3, 10, 10, 20, 5);
  10741.   cputs ("\n\rPulsa una tecla para copiar recuadro (5,3,75,22) a memoria");
  10742.   getch ();
  10743.   gettext (5, 3, 75, 22, buffer);
  10744.   cputs ("\n\rPulsa una tecla para borrar pantalla");
  10745.   getch ();
  10746.   window (1, 1, 80, 25);
  10747.   textattr (0x70);
  10748.   clrscr ();
  10749.   cputs ("Pulsa una tecla para copiar recuadro de memoria a (6,4,76,23)");
  10750.   getch ();
  10751.   puttext (6, 4, 76, 23, buffer);
  10752.   getch ();
  10753. }
  10754. ende
  10755. begine " EJEMPLO SOBRE ALGUNAS FUNCIONES GRAFICAS DE TURBO C "
  10756. /*
  10757.   Este programa prueba la mayoría de las funciones gráficas de Turbo C.
  10758.   La única utilidad de este programa es mostrar al usuario el uso de las
  10759.   funciones gráficas.
  10760. */
  10761.  
  10762. #include <graphics.h>
  10763. #include <stdio.h>
  10764. #include <stdlib.h>
  10765. #include <conio.h>
  10766. #include <alloc.h>
  10767.  
  10768. void func_arc (void);
  10769. void func_bar (void);
  10770. void func_bar3d (void);
  10771. void func_circle (void);
  10772. void func_drawpoly (void);
  10773. void func_ellipse (void);
  10774. void func_fillellipse (void);
  10775. void func_fillpoly (void);
  10776. void func_floodfill (void);
  10777. void funcs_getaspectratio_y_setaspectratio (void);
  10778. void func_line (void);
  10779. void func_linerel (void);
  10780. void func_lineto (void);
  10781. void func_pieslice (void);
  10782. void funcs_putimage_y_getimage (void);
  10783. void func_putpixel (void);
  10784. void func_rectangle (void);
  10785. void func_restorecrtmode (void);
  10786. void func_sector (void);
  10787. void funcs_setfillstyle_y_setfillpattern (void);
  10788. void func_setlinestyle (void);
  10789. void func_settextstyle (void);
  10790. void func_settextjustify (void);
  10791. void fun_setusercharsize (void);
  10792. void func_setwritemode (void);
  10793.  
  10794. int colormax, xmax, ymax, mitx, mity, terx, tery, numfuncs, indice;
  10795.  
  10796. struct
  10797.   {
  10798.     void (*func) (void);
  10799.     char *nomfunc;
  10800.   } funcs[] =
  10801.   {
  10802.     { func_arc, "arc" },
  10803.     { func_bar, "bar" },
  10804.     { func_bar3d, "bar3d" },
  10805.     { func_circle, "circle" },
  10806.     { func_drawpoly, "drawpoly" },
  10807.     { func_ellipse, "ellipse" },
  10808.     { func_fillellipse, "fillelipse" },
  10809.     { func_fillpoly, "fillpoly" },
  10810.     { func_floodfill, "floodfill" },
  10811.     { funcs_getaspectratio_y_setaspectratio, "getaspectratio() y función setaspectratio" },
  10812.     { func_line, "line" },
  10813.     { func_linerel, "linerel" },
  10814.     { func_lineto, "lineto" },
  10815.     { func_pieslice, "pieslice" },
  10816.     { funcs_putimage_y_getimage, "putimage() y función getimage" },
  10817.     { func_putpixel, "putpixel" },
  10818.     { func_rectangle, "rectangle" },
  10819.     { func_restorecrtmode, "rectorecrtmode" },
  10820.     { func_sector, "sector" },
  10821.     { funcs_setfillstyle_y_setfillpattern, "setfillstyle() y función setfillpattern" },
  10822.     { func_setlinestyle, "setlinestyle" },
  10823.     { func_settextstyle, "settextstyle" },
  10824.     { func_settextjustify, "settextjustify" },
  10825.     { fun_setusercharsize, "setusercharsize" },
  10826.     { func_setwritemode, "setwritemode" }
  10827.   };
  10828.  
  10829. #define ESC 27
  10830. #define LEFT 75
  10831. #define RIGHT 77
  10832. #define ENTER 13
  10833.  
  10834. void main (void)
  10835. {
  10836.   int driver_grafico = DETECT, modo_grafico, codigo_de_error;
  10837.   initgraph (&driver_grafico, &modo_grafico, "");
  10838.   codigo_de_error = graphresult ();
  10839.   if (codigo_de_error != grOk)
  10840.     {
  10841.       printf ("Error gráfico: %s\n", grapherrormsg (codigo_de_error));
  10842.       printf ("Presiona una tecla para finalizar");
  10843.       getch();
  10844.     }
  10845.   else
  10846.     {
  10847.       int salir = 0;
  10848.       char ch;
  10849.       char titulo[81];
  10850.       numfuncs = sizeof (funcs) / sizeof (*funcs);
  10851.       indice = 0;
  10852.       colormax = getmaxcolor ();
  10853.       xmax = getmaxx ();
  10854.       ymax = getmaxy ();
  10855.       mitx = xmax / 2;
  10856.       mity = ymax / 2;
  10857.       terx = xmax / 3;
  10858.       tery = ymax / 3;
  10859.       setcolor (colormax);
  10860.       do
  10861.         {
  10862.           cleardevice ();
  10863.           sprintf (titulo, "Función %s()", funcs[indice].nomfunc);
  10864.           outtext (titulo);
  10865.           (*funcs[indice].func) ();
  10866.           if ((ch = getch ()) == ESC)
  10867.             salir = 1;
  10868.           else
  10869.             if (ch == 0)
  10870.               if ((ch = getch ()) == LEFT)
  10871.                 indice--;
  10872.               else
  10873.                 if (ch == RIGHT)
  10874.                   indice++;
  10875.           if (indice < 0)
  10876.             indice = numfuncs - 1;
  10877.           else
  10878.             if (indice > numfuncs - 1)
  10879.               indice = 0;
  10880.         } while (! salir);
  10881.       closegraph ();
  10882.     }
  10883. }
  10884.  
  10885. /*
  10886.   PODEMOS INCLUIR ESTAS DOS FUNCIONES PARA QUE SEAMOS NOSOTROS LOS QUE
  10887.   ASIGNEMOS Y DESASIGNEMOS LA MEMORIA NECESARIA PARA TRABAJAR CON GRAFICOS.
  10888.  
  10889.   NOTA: PARA COMPILAR ESTE PROGRAMA TAL Y COMO ESTA, DEBE PONER LA OPCION
  10890.   DE ANIDAR COMENTARIO A ON. SI TU VERSION DE TURBO C NO TIENE LA DIRECTIVA
  10891.   #pragma argsused, PUEDES UTILIZAR LA DIRECTIVA #pragma warn -xxx CORRES-
  10892.   PONDIENTE.
  10893.  
  10894. /* llamada por el núcleo gráfico para asignar memoria */
  10895. void far * far _graphgetmem (unsigned tam)
  10896. {
  10897.   /* asigna memoria de montón far */
  10898.   return farmalloc (tam);
  10899. }
  10900.  
  10901. #pragma argsused /* para evitar warning de parámetro tam no usado */
  10902. /* llamado por el núcleo gráfico para liberar memoria */
  10903.  
  10904. void far _graphfreemem (void far *ptr, unsigned tam)
  10905. {
  10906.   /* libera ptr de montón far */
  10907.   farfree (ptr);
  10908. }
  10909. */
  10910.  
  10911. void func_arc (void)
  10912. {
  10913.   arc (mitx, mity, 45, 90+45, tery);
  10914.   arc (mitx, mity, 180+45, -45, tery);
  10915. }
  10916.  
  10917. void func_bar (void)
  10918. {
  10919.   bar (mitx-tery, mity-tery, mitx+tery, mity+tery);
  10920. }
  10921.  
  10922. void func_bar3d (void)
  10923. {
  10924.   bar3d (mitx-tery, mity-tery, mitx+tery, mity+tery, ymax/5, 1);
  10925. }
  10926.  
  10927. void func_circle (void)
  10928. {
  10929.   circle (mitx, mity, tery);
  10930. }
  10931.  
  10932. void func_drawpoly (void)
  10933. {
  10934.   int poligono[10];
  10935.   poligono[0] = 20;
  10936.   poligono[1] = 20;
  10937.   poligono[2] = xmax - 20;
  10938.   poligono[3] = 50;
  10939.   poligono[4] = xmax - 20;
  10940.   poligono[5] = ymax - 50;
  10941.   poligono[6] = 90;
  10942.   poligono[7] = ymax - 70;
  10943.   poligono[8] = poligono[0];
  10944.   poligono[9] = poligono[1];
  10945.   drawpoly (5, poligono);
  10946. }
  10947.  
  10948. void func_ellipse (void)
  10949. {
  10950.   ellipse (mitx, mity, 45, -45, terx, tery);
  10951. }
  10952.  
  10953. void func_fillellipse (void)
  10954. {
  10955.   fillellipse (mitx, mity, terx, tery);
  10956. }
  10957.  
  10958. void func_fillpoly (void)
  10959. {
  10960.   int poligono[10];
  10961.   poligono[0] = 20;
  10962.   poligono[1] = 20;
  10963.   poligono[2] = xmax - 20;
  10964.   poligono[3] = 50;
  10965.   poligono[4] = xmax - 20;
  10966.   poligono[5] = ymax - 50;
  10967.   poligono[6] = 90;
  10968.   poligono[7] = ymax - 70;
  10969.   poligono[8] = poligono[0];
  10970.   poligono[9] = poligono[1];
  10971.   fillpoly (5, poligono);
  10972. }
  10973.  
  10974. void func_floodfill (void)
  10975. {
  10976.   rectangle (mitx, 0, xmax, ymax);
  10977.   circle (xmax - 20, mity, 20);
  10978.   circle (mitx + 20, mity, 50);
  10979.   ellipse (mitx + 100, ymax - 50, 0, 360, 150, 50);
  10980.   rectangle (mitx + 30, 30, xmax - 30, 50);
  10981.   floodfill (mitx + 1, 1, colormax);
  10982. }
  10983.  
  10984. void funcs_getaspectratio_y_setaspectratio (void)
  10985. {
  10986.   int cuadx, cuady;
  10987.  
  10988.   getaspectratio (&cuadx, &cuady);
  10989.  
  10990.   circle (mitx, mity, 100); /* dibuja círculo normal */
  10991.   setaspectratio (cuadx/2, cuady);
  10992.   circle (mitx, mity, 100); /* dibuja círculo ancho */
  10993.   setaspectratio (cuadx, cuady/2); /* dibuja círculo alto */
  10994.   circle (mitx, mity, 100);
  10995.  
  10996.   setaspectratio (cuadx, cuady);
  10997. }
  10998.  
  10999. void func_line (void)
  11000. {
  11001.   line (mitx, 0, mitx, ymax);
  11002.   line (0, mity, xmax, mity);
  11003.   line (0, 0, xmax, ymax);
  11004.   line (xmax, 0, 0, ymax);
  11005. }
  11006.  
  11007. void func_linerel (void)
  11008. {
  11009.   moveto (0, 0);
  11010.   linerel (mitx, ymax);
  11011.   linerel (mitx, -ymax);
  11012.   linerel (0, ymax);
  11013.   linerel (-mitx, -ymax);
  11014.   linerel (-mitx, ymax);
  11015.   linerel (0, -ymax);
  11016. }
  11017.  
  11018. void func_lineto (void)
  11019. {
  11020.   moveto (0, 0);
  11021.   lineto (0, ymax);
  11022.   lineto (xmax, 0);
  11023.   lineto (xmax, ymax);
  11024.   lineto (0, 0);
  11025. }
  11026.  
  11027. void func_pieslice (void)
  11028. {
  11029.   pieslice (mitx, mity, 60, 180, tery);
  11030. }
  11031.  
  11032. #define TAMANIO_FLECHA 10
  11033.  
  11034. void funcs_putimage_y_getimage (void)
  11035. {
  11036.   void dibujar_flecha (int x, int y);
  11037.   void *flecha;
  11038.   int x, y;
  11039.   unsigned int tamanio;
  11040.   char ch;
  11041.  
  11042.   x = 0;
  11043.   y = mity;
  11044.  
  11045.   outtext ("   [Pulsa ENTER para mover flecha]");
  11046.  
  11047.   /* dibuja la imagen para ser grabada */
  11048.   dibujar_flecha (x, y);
  11049.  
  11050.   /* calcula el tamaño de la imagen */
  11051.   tamanio = imagesize (x, y-TAMANIO_FLECHA, x+(4*TAMANIO_FLECHA), y+TAMANIO_FLECHA);
  11052.  
  11053.   /* allocate memory to hold the image */
  11054.   if ((flecha = malloc (tamanio)) == NULL)
  11055.     {
  11056.       closegraph ();
  11057.       puts ("Memoria insuficiente.");
  11058.       exit (1);
  11059.     }
  11060.  
  11061.   /* graba la imagen */
  11062.   getimage (x, y-TAMANIO_FLECHA, x+(4*TAMANIO_FLECHA), y+TAMANIO_FLECHA, flecha);
  11063.  
  11064.   /* repite hasta que es presionada una tecla distinta de ENTER */
  11065.   while ((ch = getch ()) == ENTER)
  11066.     {
  11067.       /* borra imagen vieja */
  11068.       putimage (x, y-TAMANIO_FLECHA, flecha, XOR_PUT);
  11069.  
  11070.       x += TAMANIO_FLECHA;
  11071.       if (x >= xmax)
  11072.           x = 0;
  11073.  
  11074.       /* escribe imagen nueva */
  11075.       putimage (x, y-TAMANIO_FLECHA, flecha, XOR_PUT);
  11076.     }
  11077.  
  11078.   ungetch (ch);
  11079.   free (flecha);
  11080. }
  11081.  
  11082. void dibujar_flecha (int x, int y)
  11083. {
  11084.   /* dibuja una flecha en la pantalla */
  11085.   moveto (x, y);
  11086.   linerel (4*TAMANIO_FLECHA, 0);
  11087.   linerel (-2*TAMANIO_FLECHA, -1*TAMANIO_FLECHA);
  11088.   linerel (0, 2*TAMANIO_FLECHA);
  11089.   linerel (2*TAMANIO_FLECHA, -1*TAMANIO_FLECHA);
  11090. }
  11091.  
  11092. void func_putpixel (void)
  11093. {
  11094.   randomize ();
  11095.   while (! kbhit())
  11096.     putpixel (random (xmax), random (ymax), random (colormax));
  11097. }
  11098.  
  11099. void func_rectangle (void)
  11100. {
  11101.   int i;
  11102.   for (i = 20; i < mitx && i < mity; i += 20)
  11103.     rectangle (i, i, xmax - i, ymax - i);
  11104. }
  11105.  
  11106. void func_restorecrtmode (void)
  11107. {
  11108.   char ch;
  11109.   outtextxy (0, mity, "Pulsa ENTER para ejecutar restorecrtmode()");
  11110.   if ((ch = getch ()) == ENTER)
  11111.     {
  11112.       restorecrtmode ();
  11113.       cputs ("Pulsa una tecla para volver al modo gráfico.");
  11114.       ch = getch ();
  11115.       setgraphmode (getgraphmode ());
  11116.     }
  11117.   ungetch (ch); /* por si se pulsa ESC, LEFT o RIGHT */
  11118. }
  11119.  
  11120. void func_sector (void)
  11121. {
  11122.   sector (mitx, mity, 45, 180 - 45, terx, tery);
  11123.   sector (mitx, mity, 180, 360, terx, tery);
  11124. }
  11125.  
  11126. void funcs_setfillstyle_y_setfillpattern (void)
  11127. {
  11128.   int numpatr = USER_FILL - EMPTY_FILL + 1;
  11129.   double gradsect = 360.0 / numpatr;
  11130.   int i;
  11131.   char patron[8] = { 0x00, 0x70, 0x20, 0x27, 0x24, 0x24, 0x07, 0x00 };
  11132.  
  11133.   for (i = EMPTY_FILL; i <= USER_FILL; i++)
  11134.     {
  11135.       if (i == USER_FILL)
  11136.          setfillpattern (patron, colormax);
  11137.       else
  11138.         setfillstyle (i, colormax);
  11139.       pieslice (mitx, mity, i * gradsect, (i + 1) * gradsect, mity - 30);
  11140.     }
  11141.  
  11142.   setfillstyle (SOLID_FILL, colormax);
  11143. }
  11144.  
  11145. void func_setlinestyle (void)
  11146. {
  11147.   char *nomestlin[] =
  11148.    {
  11149.      "SOLID_LINE",
  11150.      "DOTTED_LINE",
  11151.      "CENTER_LINE",
  11152.      "DASHED_LINE",
  11153.      "USERBIT_LINE"
  11154.      };
  11155.    char *nomlinanch = "SOLID_LINE Y THICK_WIDTH";
  11156.    int estilo, patron_de_usuario, posytexto, posylinea;
  11157.  
  11158.    /* un patrón de línea definido por el usuario */
  11159.    /* en binario: "0000000000000001" */
  11160.    patron_de_usuario = 1;
  11161.  
  11162.    for (estilo = SOLID_LINE; estilo <= USERBIT_LINE; estilo++)
  11163.      {
  11164.        posytexto = textheight (nomestlin[estilo]) * (estilo + 1) * 5;
  11165.        posylinea = posytexto + textheight (nomestlin[estilo]) / 2;
  11166.        setlinestyle (estilo, patron_de_usuario, 1);
  11167.        line (0, posylinea, mitx, posylinea);
  11168.        outtextxy (mitx + 10, posytexto, nomestlin[estilo]);
  11169.      }
  11170.  
  11171.    posytexto = textheight (nomlinanch) * (estilo + 1) * 5;
  11172.    posylinea = posytexto + textheight (nomlinanch) / 2;
  11173.    setlinestyle (SOLID_LINE, 0, THICK_WIDTH);
  11174.    line (0, posylinea, mitx, posylinea);
  11175.    outtextxy (mitx + 10, posytexto, nomlinanch);
  11176.  
  11177.    setlinestyle (SOLID_LINE, 0, NORM_WIDTH);
  11178. }
  11179.  
  11180. void func_settextstyle (void)
  11181. {
  11182.   char *nomest[] =
  11183.     {
  11184.       "DEFAULT",
  11185.       "TRIPLEX",
  11186.       "SMALL",
  11187.       "SANS SERIF",
  11188.       "GOTHIC"
  11189.     };
  11190.   char *nomdir[] =
  11191.     {
  11192.       "HORIZ_DIR",
  11193.       "VERT_DIR"
  11194.     };
  11195.   int tamanio, estilo, direccion;
  11196.   char msj[81];
  11197.   char ch;
  11198.  
  11199.   for (;;)
  11200.     for (tamanio = 1; tamanio <= 100; tamanio++)
  11201.       for (estilo = DEFAULT_FONT + 1; estilo <= GOTHIC_FONT; estilo++)
  11202.         for (direccion = HORIZ_DIR; direccion <= VERT_DIR; direccion++)
  11203.           {
  11204.             cleardevice ();
  11205.             graphdefaults ();
  11206.             sprintf (msj, "Función %s()", funcs[indice].nomfunc);
  11207.             outtext (msj);
  11208.             outtext ("  [Pulsa ENTER para siguiente estilo]");
  11209.             settextjustify (CENTER_TEXT, CENTER_TEXT);
  11210.             settextstyle (estilo, direccion, tamanio);
  11211.             sprintf (msj, "Estilo %s, tamaño %d, dirección %s",
  11212.                      nomest[estilo], tamanio, nomdir[direccion]);
  11213.             outtextxy (mitx, mity, msj);
  11214.             if ((ch = getch ()) != ENTER) /* por si se pulsa ESC, LEFT o RIGHT */
  11215.               {
  11216.                 ungetch (ch);
  11217.                 graphdefaults ();
  11218.                 return;
  11219.               }
  11220.           }
  11221. }
  11222.  
  11223. void func_settextjustify (void)
  11224. {
  11225.   char msj[80];
  11226.   char *justh[] = { "LEFT_TEXT", "CENTER_TEXT", "RIGHT_TEXT" };
  11227.   char *justv[] = { "BOTTOM_TEXT", "CENTER_TEXT", "TOP_TEXT" };
  11228.   int jh, jv;
  11229.   char ch;
  11230.  
  11231.   for (;;)
  11232.     for (jh = LEFT_TEXT; jh <= RIGHT_TEXT; jh++)
  11233.       for (jv = BOTTOM_TEXT; jv <= TOP_TEXT; jv++)
  11234.         {
  11235.           cleardevice ();
  11236.           graphdefaults ();
  11237.           sprintf (msj, "Función %s()", funcs[indice].nomfunc);
  11238.           outtext (msj);
  11239.           outtext ("  [Pulsa ENTER para siguiente justificación]");
  11240.           settextjustify (jh, jv);
  11241.           sprintf (msj, "%s  %s", justh[jh], justv[jv]);
  11242.           line (mitx - 4, mity, mitx + 4, mity);
  11243.           line (mitx, mity - 4, mitx, mity + 4);
  11244.           outtextxy (mitx, mity, msj);
  11245.           if ((ch = getch ()) != ENTER) /* por si se pulsa ESC, LEFT o RIGHT */
  11246.             {
  11247.               ungetch (ch);
  11248.               graphdefaults ();
  11249.               return;
  11250.             }
  11251.         }
  11252. }
  11253.  
  11254. void fun_setusercharsize (void)
  11255. {
  11256.    /* selecciona un estilo de texto */
  11257.    settextstyle (TRIPLEX_FONT, HORIZ_DIR, 4);
  11258.  
  11259.    /* mueve a la posición inicial del texto */
  11260.    moveto (0, tery);
  11261.  
  11262.    /* texto normal */
  11263.    outtext ("Normal ");
  11264.  
  11265.    /* hace la anchura del texto 1/3 de la normal */
  11266.    setusercharsize (1, 3, 1, 1);
  11267.    outtext ("Corto ");
  11268.  
  11269.    /* hace la anchura del texto 3 veces la normal */
  11270.    setusercharsize (3, 1, 1, 1);
  11271.    outtext ("Ancho ");
  11272.  
  11273.    /* hace la altura del texto 1/3 de la normal */
  11274.    setusercharsize (1, 1, 1, 3);
  11275.    outtext ("Bajo ");
  11276.  
  11277.    /* hace la altura del texto 3 veces la normal */
  11278.    setusercharsize (1, 1, 3, 1);
  11279.    outtext ("Alto ");
  11280.  
  11281.    moveto (0, mity);
  11282.  
  11283.    /* hace la anchura y la altura 3 veces la normal */
  11284.    setusercharsize (3, 1, 3, 1);
  11285.    outtext ("Grande");
  11286.  
  11287.    /* hace la anchura 6 veces la normal y la altura 4 veces la normal */
  11288.    setusercharsize (6, 1, 4, 1);
  11289.    outtextxy (0, 0, "Gigante");
  11290.  
  11291.    graphdefaults ();
  11292. }
  11293.  
  11294. void func_setwritemode (void)
  11295. {
  11296.   setwritemode (COPY_PUT);
  11297.   fillellipse (mitx, mity, terx, tery);
  11298.   line (0, 0, xmax, ymax);
  11299.   setwritemode (XOR_PUT);
  11300.   fillellipse (mitx, mity, terx, tery);
  11301.   line (0, ymax, xmax, 0);
  11302.   graphdefaults ();
  11303. }
  11304. ende
  11305. endt
  11306. begine " LISTADOR DE FICHEROS EN HEXADECIMAL Y EN ASCII "
  11307. /*
  11308.   Este programa lista el contenido de un fichero, bien de texto o bien
  11309.   binario, en hexadecimal y en ASCII. Los caracteres no imprimibles se
  11310.   imprimen con un '.'.
  11311.  
  11312.   La versión del main() sin argumentos se ha suministrado para ejecutar
  11313.   este ejemplo desde el tutor. Si este ejemplo se copia en un fichero y
  11314.   se convierte en un fichero ejecutable es preferible utilizar la primera
  11315.   versión del main() (la que está ahora entre comentarios) por ser más
  11316.   práctica.
  11317. */
  11318.  
  11319. /*
  11320. #include <stdio.h>  /* fprintf (), stderr, fopen (), NULL, EOF, putchar () */
  11321. #include <ctype.h>  /* isprint ()                                          */
  11322. #include <stdlib.h> /* exit ()                                             */
  11323.  
  11324. void main (int argc, char *argv[])
  11325. {
  11326.   FILE *fich;
  11327.   register int i;
  11328.   int buffer[16];
  11329.   unsigned long desplazamiento_en_fichero = 0;
  11330.  
  11331.   if (argc != 2)
  11332.     {
  11333.       fprintf (stderr, "\nSintaxis: nombre_programa nombre_fichero\n");
  11334.       exit (1);
  11335.     }
  11336.  
  11337.   if ((fich = fopen (argv[1], "rb")) == NULL)
  11338.     {
  11339.       fprintf (stderr, "\nNo se puede abrir el fichero %s.\n", argv[1]);
  11340.       exit (1);
  11341.     }
  11342.  
  11343.   for (; ! feof (fich); desplazamiento_en_fichero += 16)
  11344.     {
  11345.       for (i = 0; i < 16; i++)
  11346.         buffer[i] = fgetc (fich);
  11347.  
  11348.       printf ("\n%04lX:", desplazamiento_en_fichero);
  11349.  
  11350.       for (i = 0; i < 16; i++)
  11351.         buffer[i] != EOF ? printf (" %02X", buffer[i]) : printf ("   ");
  11352.  
  11353.       printf ("   ");
  11354.  
  11355.       for (i = 0; i < 16; i++)
  11356.         putchar (buffer[i] != EOF ? (isprint (buffer[i]) ? buffer[i] : '.') : ' ');
  11357.     }
  11358.  
  11359.   putchar ('\n');
  11360.  
  11361.   fclose (fich);
  11362. }
  11363. */
  11364.  
  11365. #include <stdio.h>  /* fprintf (), stderr, fopen (), NULL, EOF, putchar (),
  11366.                        scanf (), puts () */
  11367. #include <ctype.h>  /* isprint () */
  11368. #include <stdlib.h> /* exit () */
  11369. #include <conio.h>  /* getch () */
  11370.  
  11371. void main (void)
  11372. {
  11373.   char nombre_fich[256];
  11374.   FILE *fich;
  11375.   register int i;
  11376.   int buffer[16];
  11377.   unsigned long desplazamiento_en_fichero = 0;
  11378.  
  11379.   puts ("LISTADOR DE FICHEROS EN HEXADECIMAL Y EN ASCII.");
  11380.   printf ("\nIntroduce fichero a listar: ");
  11381.   scanf ("%s", nombre_fich);
  11382.  
  11383.   if ((fich = fopen (nombre_fich, "rb")) == NULL)
  11384.     {
  11385.       fprintf (stderr, "\nNo se puede abrir el fichero %s.\n", nombre_fich);
  11386.       getch ();
  11387.       exit (1);
  11388.     }
  11389.  
  11390.   for (; ! feof (fich); desplazamiento_en_fichero += 16)
  11391.     {
  11392.       for (i = 0; i < 16; i++)
  11393.         buffer[i] = fgetc (fich);
  11394.  
  11395.       printf ("\n%04lX:", desplazamiento_en_fichero);
  11396.  
  11397.       for (i = 0; i < 16; i++)
  11398.         buffer[i] != EOF ? printf (" %02X", buffer[i]) : printf ("   ");
  11399.  
  11400.       printf ("   ");
  11401.  
  11402.       for (i = 0; i < 16; i++)
  11403.         putchar (buffer[i] != EOF ? (isprint (buffer[i]) ? buffer[i] : '.') : ' ');
  11404.     }
  11405.  
  11406.   putchar ('\n');
  11407.  
  11408.   fclose (fich);
  11409.  
  11410.   getch ();
  11411. }
  11412. ende
  11413. end lección 10
  11414.  
  11415. ; LECCION 11
  11416. begin
  11417. begine " EJEMPLO PARA PROBAR LAS FUNCIONES DE SETJMP.H "
  11418. /*
  11419.   Ejemplo para ver cómo funciona las funciones de setjmp.h.
  11420. */
  11421.  
  11422. #include <stdio.h>
  11423. #include <setjmp.h>
  11424. #include <stdlib.h>
  11425. #include <conio.h>
  11426.  
  11427. void func (jmp_buf);
  11428.  
  11429. void main (void)
  11430. {
  11431.   int valor;
  11432.   jmp_buf jmpb;
  11433.  
  11434.   printf ("1 ");
  11435.   valor = setjmp (jmpb);
  11436.   if (valor != 0)
  11437.     {
  11438.       printf ("3 ");
  11439.       getch ();
  11440.       exit (valor);
  11441.     }
  11442.   printf ("2 ");
  11443.   func (jmpb);
  11444.   printf ("4 ");
  11445. }
  11446.  
  11447. void func (jmp_buf jmpb)
  11448. {
  11449.   longjmp (jmpb, 1);
  11450. }
  11451. ende
  11452. begint
  11453. begine " DIR "
  11454. /*
  11455.   Lista los ficheros con una determinada máscara del directorio dado.
  11456. */
  11457.  
  11458. #include <stdio.h>
  11459. #include <dir.h>
  11460. #include <conio.h>
  11461. #include <time.h>
  11462.  
  11463. void main (void)
  11464. {
  11465.    struct ffblk ffblk;
  11466.    int mas_ficheros;
  11467.    char path[MAXPATH];
  11468.  
  11469.    clrscr ();
  11470.  
  11471.    printf ("Introduce máscara de ficheros a listar: ");
  11472.    gets (path);
  11473.  
  11474.    mas_ficheros = findfirst (path, &ffblk, 0);
  11475.    while (! mas_ficheros)
  11476.      {
  11477.        printf ("\n%-13s %9ld", ffblk.ff_name, ffblk.ff_fsize);
  11478.        mas_ficheros = findnext (&ffblk);
  11479.      }
  11480.  
  11481.    putchar ('\n');
  11482.    getch ();
  11483. }
  11484. ende
  11485. begine " EJEMPLO PARA PROBAR LAS FUNCIONES int86() Y bdos() "
  11486. /*
  11487.   Programa ejemplo para probar la utilización de las funciones
  11488.   int86() y bdos().
  11489. */
  11490.  
  11491. #include <dos.h>   /* para utilizar int86() y bdos() */
  11492. #include <stdio.h> /* para utilizar printf() */
  11493.  
  11494. void posicionar (int x, int y);
  11495. void borrar_ventana (int x1, int y1, int x2, int y2);
  11496. void borrar_pantalla (void);
  11497. void leer_version_dos (void);
  11498. void escribir_version_dos (void);
  11499. char leer_caracter (void);
  11500. void escribir_caracter (int ch);
  11501. void leer_posicion_del_cursor (void);
  11502. void escribir_posicion_del_cursor (void);
  11503. void preguntar_posicion_del_cursor (void);
  11504. void fijar_posicion_del_cursor (void);
  11505.  
  11506. union REGS regs;
  11507. int ch1, ch2, version_dos;
  11508.  
  11509. void main (void)
  11510. {
  11511.   borrar_pantalla ();
  11512.   leer_version_dos ();
  11513.   escribir_version_dos ();
  11514.   leer_posicion_del_cursor ();
  11515.   escribir_posicion_del_cursor ();
  11516.   do
  11517.     {
  11518.       preguntar_posicion_del_cursor ();
  11519.       fijar_posicion_del_cursor ();
  11520.       printf ("\n\nPulsa ESC para salir o cualquier otra tecla para continuar");
  11521.     } while (leer_caracter () != 27);
  11522. }
  11523.  
  11524. void posicionar (int x, int y)
  11525. {
  11526.   regs.h.ah = 2; /* fijar posición cursor */
  11527.   regs.h.dh = y; /* fila */
  11528.   regs.h.dl = x; /* columna */
  11529.   regs.h.bh = 0; /* número de página */
  11530.   int86 (0x10, ®s, ®s);
  11531. }
  11532.  
  11533. void borrar_ventana (int x1, int y1, int x2, int y2)
  11534. {
  11535.   regs.h.ah = 6; /* desplazamiento vertical ascendente (scroll up) */
  11536.   regs.h.al = 0; /* borrar ventana entera */
  11537.   regs.h.cl = x1; /* columna izquierda */
  11538.   regs.h.ch = y1; /* fila superior */
  11539.   regs.h.dl = x2; /* columna derecha */
  11540.   regs.h.dh = y2; /* fila inferior */
  11541.   regs.h.bh = 7; /* atributo de visualización para las líneas nuevas */
  11542.   int86 (0x10, ®s, ®s);
  11543. }
  11544.  
  11545. void borrar_pantalla (void)
  11546. {
  11547.   borrar_ventana (0, 0, 80, 25);
  11548.   posicionar (0, 0);
  11549. }
  11550.  
  11551. void leer_version_dos (void)
  11552. {
  11553.   version_dos = bdos (0x30, 0, 0);
  11554. }
  11555.  
  11556. void escribir_version_dos (void)
  11557. {
  11558.   printf ("\nVersión corriente del DOS: %d.%d\n", version_dos & 0x00FF,
  11559.           (version_dos & 0xFF00) >> 8);
  11560. }
  11561.  
  11562. char leer_caracter (void)
  11563. {
  11564.   regs.h.ah = 0; /* lee el siguiente carácter del teclado */
  11565.   int86 (0x16, ®s, ®s);
  11566.   return (regs.h.al);
  11567. }
  11568.  
  11569. void escribir_caracter (int ch)
  11570. {
  11571.   regs.h.ah = 9; /* escribe carácter y atributo */
  11572.   regs.h.al = ch; /* carácter */
  11573.   regs.h.bh = 0; /* número de página */
  11574.   regs.h.bl = 7; /* atributo */
  11575.   regs.x.cx = 1; /* número de caracteres a repetir */
  11576.   int86 (0x10, ®s, ®s);
  11577. }
  11578.  
  11579. void leer_posicion_del_cursor (void)
  11580. {
  11581.   regs.h.ah = 3; /* lee la posición del cursor */
  11582.   regs.h.bh = 0; /* número de página del display */
  11583.   int86 (0x10, ®s, ®s);
  11584. }
  11585.  
  11586. void escribir_posicion_del_cursor (void)
  11587. {
  11588.   printf ("\nlínea de comienzo del cursor: %d", regs.h.ch);
  11589.   printf ("\nlínea de final de cursor: %d", regs.h.cl);
  11590. }
  11591.  
  11592. void preguntar_posicion_del_cursor (void)
  11593. {
  11594.   printf ("\n\n\nIntroduzca línea de comienzo del cursor [0-7]: ");
  11595.   while ((ch1=leer_caracter()-'0') < 0 || ch1 > 7)
  11596.     ;
  11597.   escribir_caracter (ch1 + '0');
  11598.   printf ("\nIntroduzca línea de final del cursor [0-7]: ");
  11599.   while ((ch2 = leer_caracter () - '0') < 0 || ch2 > 7)
  11600.     ;
  11601.   escribir_caracter (ch2 + '0');
  11602. }
  11603.  
  11604. void fijar_posicion_del_cursor (void)
  11605. {
  11606.   regs.h.ah = 1; /* ajusta el tamaño del cursor */
  11607.   regs.h.ch = ch1; /* línea de comienzo */
  11608.   regs.h.cl = ch2; /* línea de final */
  11609.   int86 (0x10, ®s, ®s);
  11610. }
  11611. ende
  11612. endt
  11613. end lección 11
  11614.  
  11615. ; LECCION 12
  11616. begin
  11617. begine " ANALISIS Y EVALUACION DE EXPRESIONES "
  11618. /*
  11619.   Este programa analiza y evalúa una expresión matemática.
  11620.  
  11621.   La expresión matemática está contenida en una cadena de caracteres y ha de
  11622.   ser una expresión matemática válida. Los espacios en blanco y tabulaciones
  11623.   son ignorados entre los elementos de la expresión. En los identificadores
  11624.   no se tiene en cuenta el caso.
  11625.  
  11626.   La expresión a evaluar puede contener las siguientes constantes:
  11627.  
  11628.     E: valor del número e
  11629.     PI: valor del número pi
  11630.  
  11631.   Las funciones permitidas en la expresión son:
  11632.  
  11633.     ** Funciones trigonométricas:        ** Funciones hiperbólicas:
  11634.     sen(x), cos(x), tg(x)                senh(x), cosh(x), tgh(x), cotgh(x)
  11635.     sec(x), cosec(x), cotg(x)            sech(x), cosech(x), cotgh(x)
  11636.     arcsen(x), arccos(x), arctg(x)          x está en radianes, si se desea
  11637.     arcsec(x), arccosec(x), arccotg(x)      en grados utilizar: gradarad()
  11638.     ** Fucniones exponenciales:
  11639.     exp(x)     e elevado a x             cuad(x)    x elevado a 2
  11640.     pot(x,y)   x elevado a y             pot10(x)   10 elevado a x
  11641.     ** Funciones Logarítmicas:
  11642.     ln(x)      logaritmo neperiano de x
  11643.     log10(x)   logaritmo decimal de x
  11644.     log(x,y)   logaritmo de x en base y
  11645.     ** Funciones varias:
  11646.     abs(x)     valor absoluto de x       rc(x)       raíz cuadrada de x
  11647.     fact(x)    factorial de x            gradarad(x) radianes de x grados
  11648.     sign(x)    0 si x=0, 1 si x>0, -1 si x<0
  11649.     max(x,y), min(x,y)   máximo y mínimo respectivamente de x e y
  11650.     resto(x,y)           resto de x / y
  11651.     hipot(x,y)           hipotenusa de los catetos x e y
  11652.  
  11653.   La sintaxis de los números es:
  11654.  
  11655.     [+ | -] digitos [.digitos] [ {E | e} [+ | -] digitos ]
  11656.  
  11657.   Los operadores por orden de menor a mayor prioridad válidos en la
  11658.   expresión son:
  11659.  
  11660.     +  -                 (binarios)
  11661.     +  -                 (monarios)
  11662.     *  /  //  \          (binarios)
  11663.     ^  **                (binarios)
  11664.     %  !                 (monarios)
  11665.  
  11666.   Consideraciones sobre la expresión:
  11667.  
  11668.     ∙ Los operadores sobre la misma línea se evaluarán de izquierda a derecha
  11669.     ∙ ^ y ** son ambos, operadores de potencia equivalentes a la función pot(x,y)
  11670.     ∙ // y \ son ambos, operadores de módulo equivalentes a la función resto(x,y)
  11671.     ∙ ! es el operador factorial equivalente a la función fact(x)
  11672.     ∙ % es el tanto por ciento
  11673.     ∙ Para cambiar las prioridades de los operadores, hacer uso de los paréntesis
  11674.  
  11675.   Examinando el programa con detalle no cuesta mucho trabajo entenderlo. No
  11676.   obstante vamos a significar que cada elemento de la expresión se denomina
  11677.   token y la variable global pt siempre apunta al principio del siguiente
  11678.   token a leer. Cada vez que se lee un token se rellena la estructura de la
  11679.   variable global tok.
  11680.  
  11681.   Aquellos usuarios que compilen este programa en un compilador distinto a
  11682.   los de Turbo C pueden tener problemas en los siguientes identificadores
  11683.   no pertenecientes al ANSI C:
  11684.  
  11685.   - Contantes M_PI y M_E definidas en math.h. La primera contiene el valor
  11686.     del número pi y la segunda contiene el valor del número e.
  11687.  
  11688.   - Función mather declarada en math.h. Esta función es un manejador de
  11689.     errores matemáticos, es decir, cada vez que ocurre un error matemático,
  11690.     el sistema la llama automáticamente.
  11691.  
  11692.   - Funciones hypot (hipotenusa) y pow10 (potencia de base 10).
  11693.  
  11694.   - Directiva #pragma warn, con la cual evitamos mensajes de warnings (avisos).
  11695. */
  11696.  
  11697. #include <stdio.h>  /* printf(), puts()   */
  11698. #include <conio.h>  /* getch () */
  11699. #include <ctype.h>  /* tolower(), isspace(), isdigit(), isalpha() */
  11700. #include <math.h>   /* funciones y tipos matemáticos */
  11701. #include <signal.h> /* signal(), SIGFPE */
  11702. #include <string.h> /* strchr(), stricmp() */
  11703. #include <stdlib.h> /* strtod(), NULL, exit() */
  11704.  
  11705. #define BOOLEAN short
  11706. #define TRUE    1
  11707. #define FALSE   0
  11708.  
  11709. #define LONG_MAX_CADENA_EXPRESION 255
  11710.  
  11711. #define esdelimitador(c) (isspace(c) || ((strchr("+-*/%^(),!\\\0",c) == NULL) ? 0 : 1))
  11712. #define max(x,y) ((x) >= (y) ? (x) : (y))
  11713. #define min(x,y) ((x) <= (y) ? (x) : (y))
  11714.  
  11715. #define NUM_PARAMETROS 0xC0
  11716. #define NUM_BITS_NUM_PAR 2
  11717. #define NUM_MAX_PARAMETROS (1 << NUM_BITS_NUM_PAR)
  11718. #define CODIGO_FUNCION 0x3F
  11719.  
  11720. enum tipo_token
  11721.   {
  11722.     OPSUM,  /* +, -        */
  11723.     OPMUL,  /* *, /, //, \ */
  11724.     OPPOT,  /* ^, **       */
  11725.     OPMON,  /* %, !        */
  11726.     NUMERO, /* long double      */
  11727.     PARAB,  /* (           */
  11728.     PARCE,  /* )           */
  11729.     CONSTANTE,
  11730.     FUNCION,
  11731.     COMA,   /* ,           */
  11732.     FIN     /* \0          */
  11733.   };
  11734.  
  11735. struct token
  11736.   {
  11737.     char token [LONG_MAX_CADENA_EXPRESION];
  11738.     enum tipo_token tipotoken;
  11739.     long double valortoken; /* en caso de que sea una constante o una variable */
  11740.   } tok; /* tiene en todo momento el token actual leído */
  11741.  
  11742. char *tabnombreconst[] = { "E", "PI", NULL };
  11743. long double tabvalorconst[] = { M_E, M_PI,  0   };
  11744.  
  11745. char *tabnombrefunc[] = { "abs", "arccos",  "arcsen","arctg", "cos",  "sen",
  11746.                           "cosh","senh",    "tg",    "tgh",   "ln",   "log10",
  11747.                           "log", "exp",     "cuad",  "rc",    "pot",  "pot10",
  11748.                           "max", "min",     "hipot", "sign",  "sec",  "cosec",
  11749.                           "fact","gradarad","cotg",  "arcsec","arccosec",
  11750.                           "arccotg",        "cotgh", "sech",  "cosech","resto",
  11751.                           NULL };
  11752.      /* los dos primeros bits indican el número de parámetros y los seis
  11753.         siguientes, el código de la función que corresponde en principio al
  11754.         lugar que ocupan en tabnombrefunc */
  11755. char  tabvalorfunc[]  = { 0x40,  0x41,     0x42,     0x43,    0x44,  0x45,
  11756.                           0x46,  0x47,     0x48,     0x49,    0x4A,  0x4B,
  11757.                           0x8C,  0x4D,     0x4E,     0x4F,    0x90,  0x51,
  11758.                           0x92,  0x93,     0x94,     0x55,    0x56,  0x57,
  11759.                           0x58,  0x59,     0x5A,     0x5B,    0x5C,
  11760.                           0x5D,            0x5E,     0x5F,    0x60,  0xA1,
  11761.                           0x00 };
  11762.  
  11763. struct
  11764.   {
  11765.     long double tabpar [NUM_MAX_PARAMETROS];
  11766.     char numpar;
  11767.   } parametros; /* información de los parámetros de la última función leída */
  11768.  
  11769. char *pt; /* puntero a la lista de tokens */
  11770.  
  11771. void main (void),
  11772.      evaluar_expresion (char *lista_tokens, long double *resultado),
  11773.      buscar_identificador (void),
  11774.      error (int coderr),
  11775.      obtener_token (void),
  11776.      expresion (long double *resultad),
  11777.      termino (long double *resulta),
  11778.      factor (long double *result),
  11779.      factorpot (long double *resul),
  11780.      elemento (long double *res),
  11781.      calcular_operandos (char operador, long double *r, long double *elem),
  11782.      calcular_funcion (char codfunc, long double *r),
  11783.      factorial (long double *r);
  11784. int matherr (struct exception *e);
  11785. void error_overflow (int subcodigo);
  11786.  
  11787. void main (void)
  11788. {
  11789.   char cadena_expresion[LONG_MAX_CADENA_EXPRESION];
  11790.   long double resultado_expresion;
  11791.  
  11792.   printf ("\nIntroduce expresión: ");
  11793.   gets (cadena_expresion);
  11794.   evaluar_expresion (cadena_expresion, &resultado_expresion);
  11795.   printf ("Resultado: %Lg", resultado_expresion);
  11796.   getch ();
  11797. }
  11798.  
  11799. void evaluar_expresion (char *lista_tokens, long double *resultado)
  11800. {
  11801.   signal (SIGFPE, error_overflow);
  11802.  
  11803.   pt = lista_tokens;
  11804.   *resultado = 0;
  11805.   obtener_token ();
  11806.   if (tok.tipotoken == FIN)
  11807.     error (1);
  11808.   else
  11809.     expresion (resultado);
  11810.   if (tok.tipotoken == PARCE) /* tok.tipotoken es PARCE, COMA ó FIN */
  11811.     error (3);
  11812.   else if (tok.tipotoken == COMA)
  11813.     error (12);
  11814. }
  11815.  
  11816. void buscar_identificador (void)
  11817. {
  11818.   BOOLEAN encontrado = FALSE;
  11819.   register int i;
  11820.  
  11821.   for (i = 0; ! encontrado && tabnombrefunc[i] != NULL; i++)
  11822.     if (stricmp (tok.token, tabnombrefunc[i]) == 0)
  11823.       encontrado = TRUE;
  11824.   if (encontrado)
  11825.     {
  11826.       tok.tipotoken = FUNCION;
  11827.       tok.valortoken = tabvalorfunc[i-1];
  11828.     }
  11829.   else
  11830.     {
  11831.       for (i = 0; ! encontrado && tabnombreconst[i] != NULL; i++)
  11832.         if (stricmp (tok.token, tabnombreconst[i]) == 0)
  11833.           encontrado = TRUE;
  11834.       if (encontrado)
  11835.         {
  11836.           tok.tipotoken = CONSTANTE;
  11837.           tok.valortoken = tabvalorconst[i-1];
  11838.         }
  11839.       else
  11840.         error (6);
  11841.     }
  11842. }
  11843.  
  11844. void error (int coderr)
  11845. {
  11846.   static char *e[] =
  11847.     {
  11848.       /*  0 */ "Error de sintaxis",
  11849.       /*  1 */ "No hay expresión",
  11850.       /*  2 */ "Carácter no válido",
  11851.       /*  3 */ "Paréntesis no emparejados",
  11852.       /*  4 */ "Nùmero no correcto (video inverso en final de número)",
  11853.       /*  5 */ "Uso incorrecto de operador",
  11854.       /*  6 */ "Identificador desconocido",
  11855.       /*  7 */ "Después del nombre de función debe ir '('",
  11856.       /*  8 */ "Función debe tener más parámetros",
  11857.       /*  9 */ "Demasiados parámetros en función",
  11858.       /* 10 */ "Función sin ')'",
  11859.       /* 11 */ "Error en el código fuente del programa",
  11860.       /* 12 */ "Carácter ',' no válido",
  11861.       /* 13 */ "Todas las funciones necesitan parámetros",
  11862.       /* 14 */ "Memoria insuficiente",
  11863.       /* 15 */ "No hay expresión entre los paréntesis",
  11864.       /* 16 */ "Error de dominio o rango en función",
  11865.       /* 17 */ "División por cero",
  11866.       /* 18 */ "Se ha producido overflow o underflow",
  11867.       /* 19 */ "Error de dominio o rango en factorial"
  11868.     };
  11869.   cprintf ("** ERROR: %s", e[coderr]);
  11870.   getch ();
  11871.   exit (1);
  11872. }
  11873.  
  11874. #pragma warn -par
  11875.  
  11876. int matherr (struct exception *e)
  11877. {
  11878.   error (16);
  11879.   return 0; /* el return nunca se ejecuta */
  11880. }
  11881.  
  11882. void error_overflow (int subcodigo)
  11883. {
  11884.   error (18);
  11885. }
  11886.  
  11887. #pragma warn +par
  11888.  
  11889. void obtener_token (void)
  11890. {
  11891.   register char *temp = tok.token;
  11892.  
  11893.   while (isspace (*pt))
  11894.     pt++;
  11895.   switch (*pt++)
  11896.     {
  11897.       case 0   : tok.tipotoken = FIN;
  11898.                  break;
  11899.       case '+' : tok.tipotoken = OPSUM;
  11900.                  *temp = '+';
  11901.                  break;
  11902.       case '-' : tok.tipotoken = OPSUM;
  11903.                  *temp = '-';
  11904.                  break;
  11905.       case '^' : tok.tipotoken = OPPOT;
  11906.                  break;
  11907.       case '*' : if (*pt == '*')
  11908.                    {
  11909.                      pt++;
  11910.                      tok.tipotoken = OPPOT;
  11911.                    }
  11912.                  else
  11913.                    {
  11914.                      tok.tipotoken = OPMUL;
  11915.                      *temp = '*';
  11916.                    }
  11917.                  break;
  11918.       case '\\': tok.tipotoken = OPMUL;
  11919.                  *temp = '\\';
  11920.                  break;
  11921.       case '/' : tok.tipotoken = OPMUL;
  11922.                  if (*pt == '/')
  11923.                    {
  11924.                      *temp = '\\';
  11925.                      pt++;
  11926.                    }
  11927.                  else
  11928.                    *temp = '/';
  11929.                  break;
  11930.       case '!' :
  11931.       case '%' : tok.tipotoken = OPMON;
  11932.                  *temp = *(pt-1);
  11933.                  break;
  11934.       case '(' : tok.tipotoken = PARAB;
  11935.                  break;
  11936.       case ')' : tok.tipotoken = PARCE;
  11937.                  break;
  11938.       case ',' : tok.tipotoken = COMA;
  11939.                  break;
  11940.       default  : if (isdigit (*--pt))
  11941.                    {
  11942.                      tok.tipotoken = NUMERO;
  11943.                      while (! esdelimitador (*pt))
  11944.                        *temp++ = *pt++;
  11945.                      *temp = 0;
  11946.                    }
  11947.                  else if (isalpha (*pt))
  11948.                    {
  11949.                      while (isalpha (*pt) && ! esdelimitador (*pt))
  11950.                        *temp++ = *pt++;
  11951.                      *temp = 0;
  11952.                      if (! esdelimitador (*pt))
  11953.                        error (0);
  11954.                      buscar_identificador ();
  11955.                    }
  11956.                  else
  11957.                    error (2);
  11958.     }
  11959. }
  11960.  
  11961. void expresion (long double *resultad)
  11962. {
  11963.   register char op = 0;
  11964.   long double sumando;
  11965.  
  11966.   if (tok.tipotoken == OPSUM)
  11967.     {
  11968.       op = *tok.token;
  11969.       obtener_token ();
  11970.     }
  11971.   termino (resultad);
  11972.   if (op == '-')
  11973.     *resultad = -(*resultad);
  11974.   while (tok.tipotoken == OPSUM)
  11975.     {
  11976.       op = *tok.token;
  11977.       obtener_token ();
  11978.       termino (&sumando);
  11979.       calcular_operandos (op, resultad, &sumando);
  11980.     }
  11981. }
  11982.  
  11983. void termino (long double *resulta)
  11984. {
  11985.   register char op;
  11986.   long double fact;
  11987.  
  11988.   factor (resulta);
  11989.   while (tok.tipotoken == OPMUL)
  11990.     {
  11991.       op = *tok.token;
  11992.       obtener_token ();
  11993.       factorpot (&fact);
  11994.       calcular_operandos (op, resulta, &fact);
  11995.     }
  11996. }
  11997.  
  11998. void factor (long double *result)
  11999. {
  12000.   long double factpot;
  12001.  
  12002.   factorpot (result);
  12003.   if (tok.tipotoken == OPPOT)
  12004.     {
  12005.       obtener_token ();
  12006.       factor (&factpot);
  12007.       calcular_operandos ('^', result, &factpot);
  12008.     }
  12009. }
  12010.  
  12011. void factorpot (long double *resul)
  12012. {
  12013.   elemento (resul);
  12014.   while (tok.tipotoken == OPMON)
  12015.     {
  12016.       switch (*tok.token)
  12017.         {
  12018.           case '%' : *resul /= 100;        break;
  12019.           case '!' : factorial (resul);    break;
  12020.           default  : error (11);
  12021.         }
  12022.       obtener_token ();
  12023.     }
  12024.   if (tok.tipotoken != OPSUM && tok.tipotoken != OPMUL && tok.tipotoken != OPPOT
  12025.      && tok.tipotoken != FIN && tok.tipotoken != PARCE && tok.tipotoken != COMA)
  12026.     error (0);
  12027. }
  12028.  
  12029. void elemento (long double *res)
  12030. {
  12031.   char *puntero_al_final; /* utilizado en la llamada a strtod() */
  12032.   char codfunc; /* código de una función */
  12033.   int numparfunc; /* número de parámetros de una función */
  12034.   int contparfunc; /* contador de parámetros de una función */
  12035.  
  12036.   switch (tok.tipotoken)
  12037.     {
  12038.       case PARAB :
  12039.         obtener_token ();
  12040.         if (tok.tipotoken == PARCE)
  12041.           error (15);
  12042.         expresion (res);
  12043.         if (tok.tipotoken != PARCE)
  12044.           error (3);
  12045.         break;
  12046.       case PARCE :
  12047.         error (3);
  12048.         break;
  12049.       case NUMERO :
  12050.         *res = strtod (tok.token, &puntero_al_final);
  12051.         if (*puntero_al_final != NULL)
  12052.           error (4);
  12053.         break;
  12054.       case CONSTANTE :
  12055.         *res = tok.valortoken;
  12056.         break;
  12057.       case FUNCION :
  12058.         codfunc = (char) tok.valortoken & CODIGO_FUNCION;
  12059.         numparfunc = ((char) tok.valortoken & NUM_PARAMETROS) >>
  12060.                      (8 - NUM_BITS_NUM_PAR);
  12061.         obtener_token ();
  12062.         if (tok.tipotoken != PARAB)
  12063.           error (7);
  12064.         obtener_token ();
  12065.         if (tok.tipotoken == PARCE)
  12066.           error (13);
  12067.         expresion (res);
  12068.         parametros.numpar = 0;
  12069.         parametros.tabpar[parametros.numpar] = *res;
  12070.         for (contparfunc = 1; contparfunc < numparfunc; contparfunc++)
  12071.           {
  12072.             if (tok.tipotoken != COMA)
  12073.               error (8);
  12074.             obtener_token ();
  12075.             expresion (res);
  12076.             parametros.tabpar[++parametros.numpar] = *res;
  12077.           }
  12078.         if (tok.tipotoken == COMA)
  12079.           error (9);
  12080.         if (tok.tipotoken != PARCE)
  12081.           error (10);
  12082.         calcular_funcion (codfunc, res);
  12083.         break;
  12084.       case OPSUM :
  12085.       case OPMUL :
  12086.       case OPPOT :
  12087.       case OPMON :
  12088.         error (5);
  12089.         break;
  12090.       case FIN :
  12091.         error (0);
  12092.         break;
  12093.       default :
  12094.         error (11);
  12095.     }
  12096.   obtener_token ();
  12097.   if (tok.tipotoken == PARAB || tok.tipotoken == FUNCION ||
  12098.       tok.tipotoken == NUMERO || tok.tipotoken == CONSTANTE)
  12099.     error (0);
  12100. }
  12101.  
  12102. void calcular_operandos (char operador, long double *r, long double *elem)
  12103. {
  12104.   switch (operador)
  12105.     {
  12106.       case '+' : *r += *elem;                              break;
  12107.       case '-' : *r -= *elem;                              break;
  12108.       case '*' : *r *= *elem;                              break;
  12109.       case '/' : if (!*elem) error (17); else *r /= *elem; break;
  12110.       case '\\': *r = fmod (*r, *elem);                    break;
  12111.       case '^' : *r = pow (*r, *elem);                     break;
  12112.       default  : error (11);
  12113.     }
  12114. }
  12115.  
  12116. void calcular_funcion (char codfuncion, long double *r)
  12117. {
  12118.   long double par1 = parametros.tabpar[0], /* parámetro 1 */
  12119.          par2 = parametros.tabpar[1]; /* parámetro 2 */
  12120.  
  12121.   switch (codfuncion)
  12122.     {
  12123.       case  0 : *r = fabs  (par1);       break;               /* abs      */
  12124.       case  1 : *r = acos  (par1);       break;               /* arccos   */
  12125.       case  2 : *r = asin  (par1);       break;               /* arcsen   */
  12126.       case  3 : *r = atan  (par1);       break;               /* arctg    */
  12127.       case  4 : *r = cos   (par1);       break;               /* cos      */
  12128.       case  5 : *r = sin   (par1);       break;               /* sen      */
  12129.       case  6 : *r = cosh  (par1);       break;               /* cosh     */
  12130.       case  7 : *r = sinh  (par1);       break;               /* senh     */
  12131.       case  8 : *r = tan   (par1);       break;               /* tg       */
  12132.       case  9 : *r = tanh  (par1);       break;               /* tgh      */
  12133.       case 10 : *r = log   (par1);       break;               /* ln       */
  12134.       case 11 : *r = log10 (par1);       break;               /* log10    */
  12135.       case 12 : *r = log10 (par1) / log10 (par2); break;      /* log      */
  12136.       case 13 : *r = exp   (par1);       break;               /* exp      */
  12137.       case 14 : *r *= *r;                break;               /* cuad     */
  12138.       case 15 : *r = sqrt  (par1);       break;               /* rc       */
  12139.       case 16 : *r = pow (par1, par2);   break;               /* pot      */
  12140.       case 17 : *r = pow10 (par1);       break;               /* pot10    */
  12141.       case 18 : *r = max (par1, par2);   break;               /* max      */
  12142.       case 19 : *r = min (par1, par2);   break;               /* min      */
  12143.       case 20 : *r = hypot (par1, par2); break;               /* hipot    */
  12144.       case 21 : *r = par1 > 0 ? 1 : par1 < 0 ? -1 : 0; break; /* sign     */
  12145.       case 22 : *r = 1 / cos(par1);      break;               /* sec      */
  12146.       case 23 : *r = 1 / sin(par1);      break;               /* cosec    */
  12147.       case 24 : *r = par1; factorial (r);break;               /* fac      */
  12148.       case 25 : *r *= M_PI / 180;        break;               /* grad     */
  12149.       case 26 : *r = 1 / tan (*r);       break;               /* cotg     */
  12150.       case 27 : *r = acos (1 / par1);    break;               /* arcsec   */
  12151.       case 28 : *r = asin (1 / par1);    break;               /* arccosec */
  12152.       case 29 : *r = atan (1 / par1);    break;               /* arccotg  */
  12153.       case 30 : *r = 1 / tanh (par1);    break;               /* cotgh    */
  12154.       case 31 : *r = 1 / cosh (par1);    break;               /* sech     */
  12155.       case 32 : *r = 1 / sinh (par1);    break;               /* cosech   */
  12156.       case 33 : *r = fmod (par1, par2);  break;               /* rest     */
  12157.       default : error (11);
  12158.     }
  12159. }
  12160.  
  12161. void factorial (long double *r)
  12162. {
  12163.   register int i;
  12164.  
  12165.   if (*r < 0 || ceil (*r) != floor (*r))
  12166.     error (19);
  12167.   else
  12168.     if (!*r)
  12169.       *r = 1;
  12170.     else
  12171.       for (i = *r - 1; i > 1; i--)
  12172.         *r *= i;
  12173. }
  12174. ende
  12175. begint
  12176. begine " LABERINTO "
  12177. /*
  12178.   Este programa consiste en el proceso buscar la salida de un laberinto. Está
  12179.   basado en la técnica de vuelta atrás (back tracking). La vuelta atrás es una
  12180.   técnica muy útil para resolver problemas de este tipo. Puesto que se necesita
  12181.   recordar dónde se ha estado, en sentido inverso, una pila es una estructura
  12182.   de datos adecuada para la vuelta atrás.
  12183.  
  12184.   El laberinto está representado por una matriz bidimensional donde cada
  12185.   posición puede tomar cuatro valores posibles:
  12186.  
  12187.   - LIBRE: representa un camino.
  12188.   - OCUPADO: representa una pared.
  12189.   - SALIDA: salida del laberinto.
  12190.   - YA_RECORRIDO: sitio por lo que ya hemos pasado anteriormente.
  12191.  
  12192.   El laberinto y la posición inicial del laberinto se generan aleatoriamente.
  12193.  
  12194.   Proceso seguido: Lo que necesitamos simular es "ir hacia atrás al útimo
  12195.   cruce e intentar ir por otro camino". Para ello ponemos todos los posibles
  12196.   movimientos desde el cuadro que se está en la pila de bifurcaciones. Cuando
  12197.   se vaya hacia atrás, el cuadro que se saque de la pila sería el siguiente
  12198.   por el que se intentaría ir en vez de por el último cruce. Los cuadros ya
  12199.   recorridos lo almacenamos en la pila de camino para ver en pantalla la
  12200.   vuelta atrás.
  12201.  
  12202.   En resumen:
  12203.  
  12204.     1. En el cuadro de comienzo examinamos los cuatro cuadros adyacentes (el
  12205.        de arriba, abajo y los dos laterales) y ponemos los que tengan LIBRE
  12206.        o SALIDA en la pila de bifurcaciones.
  12207.  
  12208.     2. Marcar los cuadros que hemos visitado poniendo YA_RECORRIDO en el
  12209.        cuadro. Esto nos protegerá de los bucles infinitos.
  12210.  
  12211.     3. Obtener de la pila el siguiente movimiento. Es decir, sacar de la
  12212.        pila de bifurcaciones. Hacer el cuadro, cuyas coordenadas hayan sido
  12213.        sacadas, el cuadro actual.
  12214.  
  12215.     4. Repetir el proceso hasta que se alcance la salida o se intente volver
  12216.        atrás y la pila de bifurcaciones esté vacía. Cuando se intente obtener
  12217.        un camino alternativo de la pila de bifurcaciones y ésta esté vacía,
  12218.        entonces no hay más posibilidades. Esto significa que no hay salida
  12219.        desde el cuadro de comienzo. Estaríamos rodeados de unos y puntos y la
  12220.        pila de bifurcaciones está vacía.
  12221.  
  12222.   En cuanto a las funciones de pila hay que decir que una pila se representa
  12223.   como una lista lineal donde se insertan y sacan elementos del principio de
  12224.   la lista. Todas estas funciones están en el primer ejemplo de la lección
  12225.   anterior, consultarlas allí si no se entiende alguna de este programa.
  12226. */
  12227.  
  12228. /* Ficheros a incluir: */
  12229.  
  12230. #include <conio.h> /* getch (), _setcursortype (), _NOCURSOR, _NORMALCURSOR,
  12231.                       textcolor (), putch (), clrscr (), gotoxy (), cprintf (),
  12232.                       definiciones de los colores                            */
  12233. #include <stdlib.h> /* randomize (), random (), exit (), EXIT_FAILURE        */
  12234. #include <time.h>   /* randomize () utiliza una función de tiempo            */
  12235. #include <alloc.h>  /* malloc (), free ()                                    */
  12236. #include <dos.h>    /* delay ()                                              */
  12237.  
  12238. /* Macros: */
  12239.  
  12240. #define BOOLEAN short
  12241. #define TRUE    1
  12242. #define FALSE   0
  12243.  
  12244. #define ESC 27
  12245.  
  12246. #define XMAX 80
  12247. #define YMAX 24
  12248.  
  12249. #define escribir_linea_estado(x,y,ms) textcolor (WHITE); gotoxy (1, YMAX+1); \
  12250.   cprintf ("Posición: (%02d,%02d); Milisegundos entre movimientos (cambiar: "\
  12251.            "\x1B\x1A): %03d; ESC salir", x, y, ms);
  12252.  
  12253. /* Tipos y variables globales: */
  12254.  
  12255. enum tipo_posicion { LIBRE, OCUPADO, SALIDA, YA_RECORRIDO };
  12256.  
  12257. typedef enum tipo_posicion etp;
  12258.  
  12259. struct nodo_pila
  12260.   {
  12261.     int x, y;
  12262.     struct nodo_pila *psig;
  12263.   };
  12264.  
  12265. typedef struct nodo_pila np;
  12266. typedef np *pnp;
  12267.  
  12268. int ms = 100; /* milisengundos entre movimientos */
  12269.  
  12270. /* Prototipos de las funciones utilizadas: */
  12271.  
  12272. void formar_laberinto_aleatorio (etp lab[YMAX+1][XMAX+1], int *px, int *py);
  12273. void escribir_laberinto (etp lab[YMAX+1][XMAX+1], int x, int y);
  12274. void buscar_salida (etp lab[YMAX+1][XMAX+1], int *px, int *py);
  12275. void procesar_posiciones_adyacentes (etp lab[YMAX+1][XMAX+1], int *px, int *py,
  12276.                                      pnp *pbif, pnp *pcam);
  12277. void hacer_movimiento (int x, int y, int color);
  12278. BOOLEAN pila_vacia (pnp pil);
  12279. void crear_nodo_pila (pnp *p);
  12280. void inicializar_pila (pnp *pil);
  12281. void apilar (pnp *pil, int x, int y);
  12282. void desapilar (pnp *pil, int *px, int *py);
  12283. void liberar_pila (pnp *pil);
  12284.  
  12285. /* Definición de las funciones utilizadas: */
  12286.  
  12287. /*
  12288.   Función principal.
  12289. */
  12290.  
  12291. void main (void)
  12292. {
  12293.   etp laberinto[YMAX+1][XMAX+1]; /* los índices 0 no se utilizan */
  12294.   int x, y; /* contiene posiciones actuales en el recorrido */
  12295.  
  12296.   _setcursortype (_NOCURSOR);
  12297.  
  12298.   randomize ();
  12299.  
  12300.   do
  12301.     {
  12302.       formar_laberinto_aleatorio (laberinto, &x, &y);
  12303.       escribir_laberinto (laberinto, x, y);
  12304.       buscar_salida (laberinto, &x, &y);
  12305.     } while (getch () != ESC);
  12306.  
  12307.   _setcursortype (_NORMALCURSOR);
  12308. }
  12309.  
  12310. /*
  12311.   Rellena la matriz del laberinto con valores aleatorios.
  12312. */
  12313.  
  12314. void formar_laberinto_aleatorio (etp lab[YMAX+1][XMAX+1], int *px, int *py)
  12315. {
  12316.   register int i, j;
  12317.  
  12318.   /* fila superior se rellena con OCUPADO */
  12319.   for (i = 1; i <= YMAX; i++)
  12320.     lab[i][1] = lab[i][XMAX] = OCUPADO;
  12321.  
  12322.   /* fila inferior se rellena con OCUPADO */
  12323.   for (j = 1; j <= XMAX; j++)
  12324.     lab[1][j] = lab[YMAX][j] = OCUPADO;
  12325.  
  12326.   /* elementos intermedios de la matriz se rellena con 0 (LIBRE) o 1 (OCUPADO) */
  12327.   for (i = 2; i <= YMAX-1; i++)
  12328.     for (j = 2; j <= XMAX-1; j++)
  12329.       lab[i][j] = (etp) random (2);
  12330.  
  12331.   /* se asigna SALIDA a: un elemento de la fila superior, un elemento de la
  12332.      fila inferior, un elemento de la primera columna y un elemento de la
  12333.      última columna; en ningún caso se asigna SALIDA a las esquinas ya que
  12334.      por ahí es imposible salir */
  12335.   lab[1][random(XMAX-2)+2] = lab[YMAX][random(XMAX-2)+2] =
  12336.     lab[random(YMAX-2)+2][1] = lab[random(YMAX-2)+2][XMAX] = SALIDA;
  12337.  
  12338.   /* genera posición inicial aleatoriamente con la condición de que caiga
  12339.      en un elemento de la matriz que sea LIBRE */
  12340.   do
  12341.     {
  12342.       *px = random (XMAX-2) + 2; /* genera valor aleatorio entre 2 y XMAX-1 */
  12343.       *py = random (YMAX-2) + 2; /* genera valor aleatorio entre 2 y YMAX-1 */
  12344.     } while (lab[*py][*px] != LIBRE);
  12345. }
  12346.  
  12347. /*
  12348.   Escribe laberinto y línea de estado en la pantalla.
  12349. */
  12350.  
  12351. void escribir_laberinto (etp lab[YMAX+1][XMAX+1], int x, int y)
  12352. {
  12353.   register int i, j;
  12354.  
  12355.   clrscr ();
  12356.  
  12357.   for (i = 1; i <= YMAX; i++)
  12358.     for (j = 1; j <= XMAX; j++)
  12359.       {
  12360.         if (lab[i][j] == SALIDA)
  12361.           textcolor (YELLOW);
  12362.         else if (i == 1 || i == YMAX || j == 1 || j == XMAX)
  12363.           textcolor (LIGHTCYAN);
  12364.         else if (lab[i][j] == OCUPADO)
  12365.           textcolor (BLUE);
  12366.         else
  12367.           textcolor (YELLOW);
  12368.         gotoxy (j, i);
  12369.         putch ('█');
  12370.       }
  12371.  
  12372.   escribir_linea_estado (x, y, ms);
  12373. }
  12374.  
  12375. /*
  12376.   Esta función busca la salida del laberinto lab a partir de la posición
  12377.   inicial (*px,*py).
  12378. */
  12379.  
  12380. void buscar_salida (etp lab[YMAX+1][XMAX+1], int *px, int *py)
  12381. {
  12382.   BOOLEAN libre, atrapado;
  12383.   pnp pila_bifurcaciones, pila_camino;
  12384.  
  12385.   inicializar_pila (&pila_bifurcaciones);
  12386.   inicializar_pila (&pila_camino);
  12387.   libre = atrapado = FALSE;
  12388.  
  12389.   hacer_movimiento (*px, *py, RED);
  12390.  
  12391.   while (! libre && ! atrapado)
  12392.     {
  12393.       lab[*py][*px] = YA_RECORRIDO;
  12394.       apilar (&pila_camino, *px, *py);
  12395.  
  12396.       procesar_posiciones_adyacentes (lab, px, py, &pila_bifurcaciones,
  12397.                                       &pila_camino);
  12398.  
  12399.       atrapado = pila_vacia (pila_bifurcaciones);
  12400.  
  12401.       if (! atrapado)
  12402.         {
  12403.           /* este bucle minimiza el proceso pero produce saltos en el recorrido del cursor;
  12404.              lo que hace este bucle es desechar aquellas bifurcaciones almacenadas en la
  12405.              pila que han sido ya recorridas por otras bifurcaciones posteriores */
  12406.           do
  12407.             {
  12408.               desapilar (&pila_bifurcaciones, px, py);  /* puede que hayamos pasado por esa casilla después de apilarla */
  12409.             } while (lab[*py][*px] == 3 && ! pila_vacia (pila_bifurcaciones));
  12410.  
  12411.           hacer_movimiento (*px, *py, RED);
  12412.         }
  12413.  
  12414.       libre = lab[*py][*px] == SALIDA;
  12415.     }
  12416.  
  12417.   liberar_pila (&pila_bifurcaciones);
  12418.   liberar_pila (&pila_camino);
  12419.  
  12420.   gotoxy (1, YMAX+1);
  12421.   textcolor (WHITE + BLINK);
  12422.   cprintf ("%-79s", libre ? "ESTOY LIBRE" : "ESTOY ATRAPADO");
  12423. }
  12424.  
  12425. /*
  12426.   Dada una determinada posición (x,y), esta función apila en pila de
  12427.   bifurcaciones todas los caminos posibles a tomar a partir de esa posición.
  12428.   Si no se puede tomar ningún camino, desapila de la pila de caminos para
  12429.   volver hacia atrás.
  12430. */
  12431.  
  12432. void procesar_posiciones_adyacentes (etp lab[YMAX+1][XMAX+1], int *px, int *py,
  12433.                                      pnp *pbif, pnp *pcam)
  12434. {
  12435.   BOOLEAN encontrada_posicion_adyacente = FALSE;
  12436.  
  12437.   while (! encontrada_posicion_adyacente)
  12438.     {
  12439.       hacer_movimiento (*px, *py, LIGHTRED); /* para deshacer el movimiento hecho al final de este bucle */
  12440.  
  12441.       if (lab[*py-1][*px] == LIBRE || lab[*py-1][*px] == SALIDA)
  12442.         {
  12443.           encontrada_posicion_adyacente = TRUE;
  12444.           apilar (pbif, *px, *py-1);
  12445.         }
  12446.  
  12447.       if (lab[*py+1][*px] == LIBRE || lab[*py+1][*px] == SALIDA)
  12448.         {
  12449.           encontrada_posicion_adyacente = TRUE;
  12450.           apilar (pbif, *px, *py+1);
  12451.         }
  12452.  
  12453.       if (lab[*py][*px-1] == LIBRE || lab[*py][*px-1] == SALIDA)
  12454.         {
  12455.           encontrada_posicion_adyacente = TRUE;
  12456.           apilar (pbif, *px-1, *py);
  12457.         }
  12458.  
  12459.       if (lab[*py][*px+1] == LIBRE || lab[*py][*px+1] == SALIDA)
  12460.         {
  12461.           encontrada_posicion_adyacente = TRUE;
  12462.           apilar (pbif, *px+1, *py);
  12463.         }
  12464.  
  12465.       if (! encontrada_posicion_adyacente)
  12466.         if (pila_vacia (*pcam))
  12467.           encontrada_posicion_adyacente = TRUE; /* salir de bucle */
  12468.         else
  12469.           {
  12470.             desapilar (pcam, px, py);
  12471.             hacer_movimiento (*px, *py, RED);
  12472.           }
  12473.     }
  12474. }
  12475.  
  12476. /*
  12477.   Realiza un movimiento. La realización de un movimiento consiste en compro-
  12478.   bar si se ha pulsado alguna tecla, si así es, leer la tecla para ver si es
  12479.   ESC, flecha izquierda o flecha derecha. A continuación escribe carácter con
  12480.   color indicado (RED o LIGHTRED) y por último escribe la línea de estado y
  12481.   genera un retardo.
  12482. */
  12483.  
  12484. void hacer_movimiento (int x, int y, int color)
  12485. {
  12486.   /* Los caracteres especiales flecha izquierda y flecha derecha generan dos
  12487.      caracteres: flecha izquierda genera 0 y 75, y flecha derecha genera 0 y 77.
  12488.      Se ha puesto while en vez de if para el caso en que se tenga la tecla
  12489.      presionada sin soltarla (en los casos de los cursores flecha izquierda y
  12490.      flecha derecha).
  12491.   */
  12492.   while (kbhit ())
  12493.     switch (getch ())
  12494.       {
  12495.         case ESC:
  12496.           exit (0);
  12497.         case 0:
  12498.           switch (getch ())
  12499.             {
  12500.               case 75:
  12501.                 if (ms > 0)
  12502.                   ms--;
  12503.                 break;
  12504.               case 77:
  12505.                 ms++;
  12506.                 break;
  12507.             }
  12508.       }
  12509.   textcolor (color);
  12510.   gotoxy (x, y);
  12511.   putch ('█');
  12512.   escribir_linea_estado (x, y, ms);
  12513.   delay (ms);
  12514. }
  12515.  
  12516. /*
  12517.   Devuelve TRUE si la pila está vacía.
  12518. */
  12519.  
  12520. BOOLEAN pila_vacia (pnp pil)
  12521. {
  12522.   return (pil == NULL);
  12523. }
  12524.  
  12525. /*
  12526.   Crea un nodo de pila (se reserva memoria para él).
  12527. */
  12528.  
  12529. void crear_nodo_pila (pnp *p)
  12530. {
  12531.   if ((*p = (pnp) malloc (sizeof (np))) == NULL)
  12532.     {
  12533.       gotoxy (1, YMAX+1);
  12534.       cprintf ("%-79s", "\nERROR: Memoria insuficiente.");
  12535.       exit (EXIT_FAILURE);
  12536.     }
  12537. }
  12538.  
  12539. /*
  12540.   Inicializa puntero a la cabeza de la pila.
  12541. */
  12542.  
  12543. void inicializar_pila (pnp *pil)
  12544. {
  12545.   *pil = NULL;
  12546. }
  12547.  
  12548. /*
  12549.   Introduce un elemento en la pila.
  12550. */
  12551.  
  12552. void apilar (pnp *pil, int x, int y)
  12553. {
  12554.   pnp p;
  12555.   crear_nodo_pila (&p);
  12556.   p->x = x;
  12557.   p->y = y;
  12558.   p->psig = *pil;
  12559.   *pil = p;
  12560. }
  12561.  
  12562. /*
  12563.   Saca un elemento de la pila.
  12564.   Nota: En este programa, al llamar a desapilar, podemos estar seguro de
  12565.   que la pila no está vacía.
  12566. */
  12567.  
  12568. void desapilar (pnp *pil, int *px, int *py)
  12569. {
  12570.   pnp p;
  12571.   *px = (*pil)->x;
  12572.   *py = (*pil)->y;
  12573.   p = *pil;
  12574.   *pil = (*pil)->psig;
  12575.   free (p);
  12576. }
  12577.  
  12578. void liberar_pila (pnp *pil)
  12579. {
  12580.   pnp p;
  12581.  
  12582.   p = *pil;
  12583.   while (p)
  12584.     {
  12585.       *pil = p->psig;
  12586.       free (p);
  12587.       p = *pil;
  12588.     }
  12589. }
  12590. ende
  12591. endt
  12592. begine " CRONOMETRAR EJECUCION DE PROCESO "
  12593. /*
  12594.   Este programa ejecuta otro un programa cronomentrando el tiempo empleado
  12595.   en la ejecución del segundo. El programa a ejecutar puede ser un programa
  12596.   ejecutable con extensión .EXE o .COM, un fichero por lotes .BAT o un co-
  12597.   mando interno del DOS como dir. Además dicho programa se puede ejecutar
  12598.   con argumentos.
  12599.  
  12600.   El main() que está entre comentarios es la versión que toma los datos desde
  12601.   la línea de órdenes del sistema operativo en vez de preguntar por ellos con
  12602.   las funciones de entrada estándar como se hace en el main() actual.
  12603. */
  12604.  
  12605. #include <process.h> /* system () */
  12606. #include <stdio.h>   /* puts (), printf () */
  12607. #include <time.h>    /* time_t, difftime (), time () */
  12608. #include <string.h>  /* strcat () */
  12609.  
  12610. /*
  12611. void main (int argc, char **argv);
  12612. */
  12613.  
  12614. void main (void);
  12615.  
  12616. void ejecutar (char *);
  12617.  
  12618. /*
  12619. void main (int argc, char **argv)
  12620. {
  12621.   if (argc == 1)
  12622.     puts ("\nEste programa cronometra el tiempo empleado al ejecutar el "
  12623.           "programa\npasado como argumento.\n");
  12624.   else
  12625.     {
  12626.       int cont;
  12627.       char nombre_programa_a_ejecutar[256] = { 0 };
  12628.       for (cont = 1; cont < argc; cont++)
  12629.         {
  12630.           if (*nombre_programa_a_ejecutar)
  12631.             strcat (nombre_programa_a_ejecutar, " ");
  12632.           strcat (nombre_programa_a_ejecutar, *(argv+cont));
  12633.         }
  12634.       ejecutar (nombre_programa_a_ejecutar);
  12635.     }
  12636. }
  12637. */
  12638.  
  12639. void main (void)
  12640. {
  12641.   #include <stdio.h> /* printf (), gets () */
  12642.   #include <conio.h> /* getch () */
  12643.  
  12644.   char nombre_programa_a_ejecutar[256] = { 0 };
  12645.   printf ("\nIntroduce nombre de programa a ejecutar: ");
  12646.   do
  12647.     {
  12648.       gets (nombre_programa_a_ejecutar);
  12649.     } while (!*nombre_programa_a_ejecutar);
  12650.   ejecutar (nombre_programa_a_ejecutar);
  12651.   getch ();
  12652. }
  12653.  
  12654. void ejecutar (char *nombre_programa)
  12655. {
  12656.   int valor_devuelto;
  12657.   time_t tiempo1, tiempo2;
  12658.   tiempo1 = time (NULL);
  12659.   valor_devuelto = system (nombre_programa);
  12660.   tiempo2 = time (NULL);
  12661.   if (valor_devuelto == -1)
  12662.     printf ("\nError al intentar ejecutar el programa \"%s\".",
  12663.             nombre_programa);
  12664.   else
  12665.     {
  12666.       int horas, minutos, segundos;
  12667.       unsigned segundos_totales = (unsigned) difftime (tiempo2, tiempo1);
  12668.       segundos = segundos_totales % 60;
  12669.       minutos = (segundos_totales / 60) % 60;
  12670.       horas = (segundos_totales / 3600) % 24;
  12671.       printf ("\nTiempo empleado en la ejecución del programa \"%s\":\n",
  12672.               nombre_programa);
  12673.       if (horas)
  12674.         printf ("%d hor%s ", horas, horas == 1 ? "a" : "as");
  12675.       if (horas || minutos)
  12676.         printf ("%d minut%s ", minutos, minutos == 1 ? "o" : "os");
  12677.       printf ("%d segund%s", segundos, segundos == 1 ? "o" : "os");
  12678.     }
  12679.   puts ("");
  12680. }
  12681. ende
  12682. end lección 12
  12683.